@dt-dds/react-icon-button 1.0.0-beta.19 → 1.0.0-beta.20
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 +6 -0
- package/README.md +108 -5
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +41 -5
- package/dist/index.mjs +41 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,18 +1,72 @@
|
|
|
1
1
|
# IconButton
|
|
2
2
|
|
|
3
|
-
This component allows the user to take action by clicking on an Icon.
|
|
3
|
+
This component allows the user to take action by clicking on an Icon. It is a flexible button component that renders an icon and can be composed with any child element through CSS overrides for advanced styling customization.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
+
### With Icon
|
|
8
|
+
|
|
7
9
|
```jsx
|
|
8
|
-
import { IconButton
|
|
10
|
+
import { IconButton } from '@dt-dds/react';
|
|
11
|
+
import { Icon } from '@dt-dds/react-icon';
|
|
9
12
|
|
|
10
13
|
export const App = () => {
|
|
11
|
-
const handleClick = () =>
|
|
14
|
+
const handleClick = () => console.log('Edit clicked');
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<IconButton
|
|
18
|
+
onClick={handleClick}
|
|
19
|
+
ariaLabel="Edit profile"
|
|
20
|
+
>
|
|
21
|
+
<Icon code='edit' />
|
|
22
|
+
</IconButton>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### With Avatar
|
|
28
|
+
|
|
29
|
+
```jsx
|
|
30
|
+
import { IconButton } from '@dt-dds/react-icon-button';
|
|
31
|
+
import { Avatar } from '@dt-dds/react-avatar';
|
|
12
32
|
|
|
33
|
+
export const AvatarButton = () => {
|
|
13
34
|
return (
|
|
14
|
-
<IconButton
|
|
15
|
-
|
|
35
|
+
<IconButton
|
|
36
|
+
ariaLabel="User profile"
|
|
37
|
+
onClick={() => console.log('Avatar clicked')}
|
|
38
|
+
>
|
|
39
|
+
<Avatar
|
|
40
|
+
title="John Doe"
|
|
41
|
+
type="letter"
|
|
42
|
+
size="medium"
|
|
43
|
+
/>
|
|
44
|
+
</IconButton>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### CSS Overrides
|
|
50
|
+
|
|
51
|
+
The cssOverrides prop allows you to apply custom CSS styles to the IconButton and its children. This enables full styling flexibility while keeping IconButton agnostic of its child components.
|
|
52
|
+
|
|
53
|
+
#### Example: Custom Icon Button Styling
|
|
54
|
+
|
|
55
|
+
```jsx
|
|
56
|
+
import { IconButton } from '@dt-dds/react-icon-button';
|
|
57
|
+
import { Icon } from '@dt-dds/react-icon';
|
|
58
|
+
import { css } from '@emotion/react';
|
|
59
|
+
|
|
60
|
+
export const CustomButton = () => {
|
|
61
|
+
const customStyles = css`
|
|
62
|
+
&:hover:not(:disabled) {
|
|
63
|
+
background-color: #ff5733;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<IconButton cssOverrides={customStyles}>
|
|
69
|
+
<Icon code='star' />
|
|
16
70
|
</IconButton>
|
|
17
71
|
);
|
|
18
72
|
};
|
|
@@ -30,6 +84,55 @@ export const App = () => {
|
|
|
30
84
|
| `isDisabled` | `boolean` | false | Determines the disabled state of the button |
|
|
31
85
|
| `variant` | `IconButtonVariant` | `default` | Determines the variant of the icon |
|
|
32
86
|
| `size` | `ComponentSize` | `large` | Determines the size of the icon |
|
|
87
|
+
| `cssOverrides`| `ReturnType<typeof css>`| - | Emotion CSS to apply custom styles to the button and its children |
|
|
88
|
+
|
|
89
|
+
### Accessibility
|
|
90
|
+
|
|
91
|
+
- Always provide ariaLabel when the button doesn't contain readable text (e.g., icon-only buttons)
|
|
92
|
+
- Focus outline is automatically visible with keyboard navigation
|
|
93
|
+
- Disabled buttons are properly marked and keyboard-inaccessible
|
|
94
|
+
- Supports screen readers through semantic button element
|
|
95
|
+
|
|
96
|
+
## Stack
|
|
97
|
+
|
|
98
|
+
- [TypeScript](https://www.typescriptlang.org/) for static type checking
|
|
99
|
+
- [React](https://reactjs.org/) — JavaScript library for user interfaces
|
|
100
|
+
- [Emotion](https://emotion.sh/docs/introduction) — for writing css styles with JavaScript
|
|
101
|
+
- [Storybook](https://storybook.js.org/) — UI component environment powered by Vite
|
|
102
|
+
- [Jest](https://jestjs.io/) - JavaScript Testing Framework
|
|
103
|
+
- [React Testing Library](https://testing-library.com/) - to test UI components in a user-centric way
|
|
104
|
+
- [ESLint](https://eslint.org/) for code linting
|
|
105
|
+
- [Prettier](https://prettier.io) for code formatting
|
|
106
|
+
- [Tsup](https://github.com/egoist/tsup) — TypeScript bundler powered by esbuild
|
|
107
|
+
- [Yarn](https://yarnpkg.com/) from managing packages
|
|
108
|
+
|
|
109
|
+
## Commands
|
|
110
|
+
|
|
111
|
+
- `yarn build` - Build the package
|
|
112
|
+
- `yarn dev` - Run the package locally
|
|
113
|
+
- `yarn lint` - Lint all files within this package
|
|
114
|
+
- `yarn test` - Run all unit tests
|
|
115
|
+
- `yarn test:report` - Open the test coverage report
|
|
116
|
+
- `yarn test:update:snapshot` - Run all unit tests and update the snapshot
|
|
117
|
+
|
|
118
|
+
## Compilation
|
|
119
|
+
|
|
120
|
+
Running `yarn build` from the root of the package will use [tsup](https://tsup.egoist.dev/) to compile the raw TypeScript and React code to plain JavaScript.
|
|
121
|
+
|
|
122
|
+
The `/dist` folder contains the compiled output.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
iconButton
|
|
126
|
+
└── dist
|
|
127
|
+
├── index.d.ts <-- Types
|
|
128
|
+
├── index.js <-- CommonJS version
|
|
129
|
+
└── index.mjs <-- ES Modules version
|
|
130
|
+
...
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Versioning
|
|
134
|
+
|
|
135
|
+
Follows [semantic versioning](https://semver.org/)
|
|
33
136
|
|
|
34
137
|
## © License
|
|
35
138
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CustomTheme } from '@dt-dds/themes';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import { BaseProps, ComponentSize } from '@dt-dds/react-core';
|
|
4
|
+
import { css } from '@emotion/react';
|
|
4
5
|
import { ComponentPropsWithoutRef } from 'react';
|
|
5
6
|
|
|
6
7
|
type IconButtonVariant = 'contrast' | 'default';
|
|
@@ -11,8 +12,9 @@ interface IconButtonProps extends ComponentPropsWithoutRef<'button'>, BaseProps
|
|
|
11
12
|
ariaLabel?: string;
|
|
12
13
|
variant?: IconButtonVariant;
|
|
13
14
|
size?: ComponentSize;
|
|
15
|
+
cssOverrides?: ReturnType<typeof css>;
|
|
14
16
|
}
|
|
15
|
-
declare const IconButton: ({ children, dataTestId, isDisabled, ariaLabel, variant, size, onClick, ...props }: IconButtonProps) => react_jsx_runtime.JSX.Element;
|
|
17
|
+
declare const IconButton: ({ children, dataTestId, isDisabled, ariaLabel, variant, size, onClick, cssOverrides, ...props }: IconButtonProps) => react_jsx_runtime.JSX.Element;
|
|
16
18
|
|
|
17
19
|
declare module '@emotion/react' {
|
|
18
20
|
interface Theme extends CustomTheme {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CustomTheme } from '@dt-dds/themes';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import { BaseProps, ComponentSize } from '@dt-dds/react-core';
|
|
4
|
+
import { css } from '@emotion/react';
|
|
4
5
|
import { ComponentPropsWithoutRef } from 'react';
|
|
5
6
|
|
|
6
7
|
type IconButtonVariant = 'contrast' | 'default';
|
|
@@ -11,8 +12,9 @@ interface IconButtonProps extends ComponentPropsWithoutRef<'button'>, BaseProps
|
|
|
11
12
|
ariaLabel?: string;
|
|
12
13
|
variant?: IconButtonVariant;
|
|
13
14
|
size?: ComponentSize;
|
|
15
|
+
cssOverrides?: ReturnType<typeof css>;
|
|
14
16
|
}
|
|
15
|
-
declare const IconButton: ({ children, dataTestId, isDisabled, ariaLabel, variant, size, onClick, ...props }: IconButtonProps) => react_jsx_runtime.JSX.Element;
|
|
17
|
+
declare const IconButton: ({ children, dataTestId, isDisabled, ariaLabel, variant, size, onClick, cssOverrides, ...props }: IconButtonProps) => react_jsx_runtime.JSX.Element;
|
|
16
18
|
|
|
17
19
|
declare module '@emotion/react' {
|
|
18
20
|
interface Theme extends CustomTheme {
|
package/dist/index.js
CHANGED
|
@@ -83,7 +83,7 @@ var iconButtonSizeStyles = {
|
|
|
83
83
|
`
|
|
84
84
|
};
|
|
85
85
|
var IconButtonStyled = import_styled.default.button(
|
|
86
|
-
({ theme, disabled, variant = "default", size = "large" }) => {
|
|
86
|
+
({ theme, disabled, variant = "default", size = "large", cssOverrides }) => {
|
|
87
87
|
const isDefaultVariant = variant === "default";
|
|
88
88
|
const baseColor = () => {
|
|
89
89
|
if (disabled) {
|
|
@@ -92,14 +92,14 @@ var IconButtonStyled = import_styled.default.button(
|
|
|
92
92
|
return isDefaultVariant ? theme.palette.content.default : theme.palette.content.contrast;
|
|
93
93
|
};
|
|
94
94
|
const hoverColor = isDefaultVariant ? theme.palette.accent.default : theme.palette.accent.medium;
|
|
95
|
-
|
|
95
|
+
const baseStyles = `
|
|
96
96
|
display: flex;
|
|
97
97
|
align-items: center;
|
|
98
98
|
border: none;
|
|
99
99
|
background-color: transparent;
|
|
100
100
|
cursor: ${disabled ? "not-allowed" : "pointer"};
|
|
101
101
|
|
|
102
|
-
i {
|
|
102
|
+
> i {
|
|
103
103
|
${iconButtonSizeStyles[size]};
|
|
104
104
|
color: ${baseColor()};
|
|
105
105
|
}
|
|
@@ -113,7 +113,40 @@ var IconButtonStyled = import_styled.default.button(
|
|
|
113
113
|
&:focus-visible {
|
|
114
114
|
outline: 2px solid ${theme.palette.primary.default};
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
&:hover:not(:disabled) {
|
|
118
|
+
[data-avatar-type='letter'] {
|
|
119
|
+
background-color: ${theme.palette.primary.dark};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[data-avatar-type='collapsed'] {
|
|
123
|
+
background-color: ${theme.palette.primary.light};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[data-avatar-type='thumbnail'] {
|
|
127
|
+
background-color: ${theme.palette.primary.dark};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
${disabled && `
|
|
132
|
+
[data-avatar-type='letter'] {
|
|
133
|
+
background-color: ${theme.palette.primary.light};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[data-avatar-type='collapsed'] {
|
|
137
|
+
background-color: ${theme.palette.surface.contrast};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
[data-avatar-type='thumbnail'] {
|
|
141
|
+
background-color: ${theme.palette.primary.light};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
[data-avatar-type='photo'] {
|
|
145
|
+
opacity: 0.38;
|
|
146
|
+
}
|
|
147
|
+
`}
|
|
116
148
|
`;
|
|
149
|
+
return cssOverrides ? [baseStyles, cssOverrides] : baseStyles;
|
|
117
150
|
}
|
|
118
151
|
);
|
|
119
152
|
|
|
@@ -127,7 +160,8 @@ var IconButton = (_a) => {
|
|
|
127
160
|
ariaLabel,
|
|
128
161
|
variant,
|
|
129
162
|
size,
|
|
130
|
-
onClick
|
|
163
|
+
onClick,
|
|
164
|
+
cssOverrides
|
|
131
165
|
} = _b, props = __objRest(_b, [
|
|
132
166
|
"children",
|
|
133
167
|
"dataTestId",
|
|
@@ -135,7 +169,8 @@ var IconButton = (_a) => {
|
|
|
135
169
|
"ariaLabel",
|
|
136
170
|
"variant",
|
|
137
171
|
"size",
|
|
138
|
-
"onClick"
|
|
172
|
+
"onClick",
|
|
173
|
+
"cssOverrides"
|
|
139
174
|
]);
|
|
140
175
|
const handleClick = (e) => {
|
|
141
176
|
if (isDisabled) {
|
|
@@ -149,6 +184,7 @@ var IconButton = (_a) => {
|
|
|
149
184
|
IconButtonStyled,
|
|
150
185
|
__spreadProps(__spreadValues({
|
|
151
186
|
"aria-label": ariaLabel,
|
|
187
|
+
cssOverrides,
|
|
152
188
|
"data-testid": dataTestId ? dataTestId : "icon-button",
|
|
153
189
|
disabled: isDisabled,
|
|
154
190
|
onClick: handleClick,
|
package/dist/index.mjs
CHANGED
|
@@ -50,7 +50,7 @@ var iconButtonSizeStyles = {
|
|
|
50
50
|
`
|
|
51
51
|
};
|
|
52
52
|
var IconButtonStyled = styled.button(
|
|
53
|
-
({ theme, disabled, variant = "default", size = "large" }) => {
|
|
53
|
+
({ theme, disabled, variant = "default", size = "large", cssOverrides }) => {
|
|
54
54
|
const isDefaultVariant = variant === "default";
|
|
55
55
|
const baseColor = () => {
|
|
56
56
|
if (disabled) {
|
|
@@ -59,14 +59,14 @@ var IconButtonStyled = styled.button(
|
|
|
59
59
|
return isDefaultVariant ? theme.palette.content.default : theme.palette.content.contrast;
|
|
60
60
|
};
|
|
61
61
|
const hoverColor = isDefaultVariant ? theme.palette.accent.default : theme.palette.accent.medium;
|
|
62
|
-
|
|
62
|
+
const baseStyles = `
|
|
63
63
|
display: flex;
|
|
64
64
|
align-items: center;
|
|
65
65
|
border: none;
|
|
66
66
|
background-color: transparent;
|
|
67
67
|
cursor: ${disabled ? "not-allowed" : "pointer"};
|
|
68
68
|
|
|
69
|
-
i {
|
|
69
|
+
> i {
|
|
70
70
|
${iconButtonSizeStyles[size]};
|
|
71
71
|
color: ${baseColor()};
|
|
72
72
|
}
|
|
@@ -80,7 +80,40 @@ var IconButtonStyled = styled.button(
|
|
|
80
80
|
&:focus-visible {
|
|
81
81
|
outline: 2px solid ${theme.palette.primary.default};
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
&:hover:not(:disabled) {
|
|
85
|
+
[data-avatar-type='letter'] {
|
|
86
|
+
background-color: ${theme.palette.primary.dark};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[data-avatar-type='collapsed'] {
|
|
90
|
+
background-color: ${theme.palette.primary.light};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[data-avatar-type='thumbnail'] {
|
|
94
|
+
background-color: ${theme.palette.primary.dark};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
${disabled && `
|
|
99
|
+
[data-avatar-type='letter'] {
|
|
100
|
+
background-color: ${theme.palette.primary.light};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-avatar-type='collapsed'] {
|
|
104
|
+
background-color: ${theme.palette.surface.contrast};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-avatar-type='thumbnail'] {
|
|
108
|
+
background-color: ${theme.palette.primary.light};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[data-avatar-type='photo'] {
|
|
112
|
+
opacity: 0.38;
|
|
113
|
+
}
|
|
114
|
+
`}
|
|
83
115
|
`;
|
|
116
|
+
return cssOverrides ? [baseStyles, cssOverrides] : baseStyles;
|
|
84
117
|
}
|
|
85
118
|
);
|
|
86
119
|
|
|
@@ -94,7 +127,8 @@ var IconButton = (_a) => {
|
|
|
94
127
|
ariaLabel,
|
|
95
128
|
variant,
|
|
96
129
|
size,
|
|
97
|
-
onClick
|
|
130
|
+
onClick,
|
|
131
|
+
cssOverrides
|
|
98
132
|
} = _b, props = __objRest(_b, [
|
|
99
133
|
"children",
|
|
100
134
|
"dataTestId",
|
|
@@ -102,7 +136,8 @@ var IconButton = (_a) => {
|
|
|
102
136
|
"ariaLabel",
|
|
103
137
|
"variant",
|
|
104
138
|
"size",
|
|
105
|
-
"onClick"
|
|
139
|
+
"onClick",
|
|
140
|
+
"cssOverrides"
|
|
106
141
|
]);
|
|
107
142
|
const handleClick = (e) => {
|
|
108
143
|
if (isDisabled) {
|
|
@@ -116,6 +151,7 @@ var IconButton = (_a) => {
|
|
|
116
151
|
IconButtonStyled,
|
|
117
152
|
__spreadProps(__spreadValues({
|
|
118
153
|
"aria-label": ariaLabel,
|
|
154
|
+
cssOverrides,
|
|
119
155
|
"data-testid": dataTestId ? dataTestId : "icon-button",
|
|
120
156
|
disabled: isDisabled,
|
|
121
157
|
onClick: handleClick,
|