@teamturing/react-kit 2.74.0 → 2.76.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/core/Datagrid/DatagridBody.d.ts +3 -3
- package/dist/core/Datagrid/DatagridCell.d.ts +8 -3
- package/dist/core/Datagrid/DatagridHeader.d.ts +2 -2
- package/dist/core/Datagrid/DatagridRow.d.ts +8 -3
- package/dist/core/Datagrid/DatagridRowList.d.ts +21 -0
- package/dist/core/Datagrid/index.d.ts +9 -5
- package/dist/core/Drawer/index.d.ts +3 -3
- package/dist/core/FormControl/index.d.ts +9 -2
- package/dist/core/Grid/index.d.ts +4 -4
- package/dist/core/Spinner/index.d.ts +12 -0
- package/dist/core/StyledIcon/index.d.ts +2 -2
- package/dist/core/Toast/index.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +251 -77
- package/dist/theme/index.d.ts +30 -0
- package/esm/core/ClickArea/index.js +20 -10
- package/esm/core/Datagrid/DatagridBody.js +2 -0
- package/esm/core/Datagrid/DatagridCell.js +3 -0
- package/esm/core/Datagrid/DatagridRow.js +13 -2
- package/esm/core/Datagrid/DatagridRowList.js +33 -0
- package/esm/core/Datagrid/index.js +26 -1
- package/esm/core/DescriptionList/index.js +5 -1
- package/esm/core/Dialog/index.js +1 -0
- package/esm/core/Drawer/index.js +7 -1
- package/esm/core/Flash/index.js +4 -1
- package/esm/core/FormControl/FormControlCaption.js +2 -2
- package/esm/core/FormControl/FormControlErrorMessage.js +3 -2
- package/esm/core/FormControl/FormControlSuccessMessage.js +3 -2
- package/esm/core/FormControl/index.js +29 -11
- package/esm/core/IconButton/index.js +8 -1
- package/esm/core/IconToggleButton/index.js +25 -15
- package/esm/core/Overlay/index.js +1 -1
- package/esm/core/SearchSelectInput/index.js +6 -1
- package/esm/core/Spinner/index.js +9 -0
- package/esm/core/StyledIcon/index.js +31 -15
- package/esm/core/Switch/index.js +1 -0
- package/esm/core/Tab/TabItem.js +1 -0
- package/esm/core/Tab/index.js +2 -0
- package/esm/core/Toast/index.js +7 -2
- package/esm/theme/index.js +10 -0
- package/package.json +2 -2
|
@@ -4,8 +4,10 @@ import { sx } from '../../utils/styled-system/index.js';
|
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
const DatagridBody = ({
|
|
7
|
+
role,
|
|
7
8
|
...props
|
|
8
9
|
}) => /*#__PURE__*/jsx(BaseDatagridBody, {
|
|
10
|
+
role: role ?? 'rowgroup',
|
|
9
11
|
...props
|
|
10
12
|
});
|
|
11
13
|
const BaseDatagridBody = /*#__PURE__*/styled.div.withConfig({
|
|
@@ -3,8 +3,11 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
|
|
4
4
|
const DatagridCell = ({
|
|
5
5
|
children,
|
|
6
|
+
columnHeader = false,
|
|
7
|
+
role,
|
|
6
8
|
...props
|
|
7
9
|
}) => /*#__PURE__*/jsx(BaseDatagridCell, {
|
|
10
|
+
role: role ?? (columnHeader ? 'columnheader' : 'cell'),
|
|
8
11
|
...props,
|
|
9
12
|
children: children
|
|
10
13
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Children, isValidElement, cloneElement } from 'react';
|
|
1
2
|
import Grid from '../Grid/index.js';
|
|
2
3
|
import Space from '../Space/index.js';
|
|
3
4
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -6,16 +7,26 @@ const DatagridRow = ({
|
|
|
6
7
|
gapX = 2,
|
|
7
8
|
alignItems,
|
|
8
9
|
justifyContent,
|
|
10
|
+
columnHeader = false,
|
|
11
|
+
role,
|
|
9
12
|
children,
|
|
10
13
|
...props
|
|
11
|
-
}) =>
|
|
14
|
+
}) =>
|
|
15
|
+
/*#__PURE__*/
|
|
16
|
+
// 외부 wrapper가 `rowgroup`의 직접 자식이 되도록 여기에 role="row"를 부여하고,
|
|
17
|
+
// 셀을 직접 감싸는 내부 Grid는 presentation 처리하여 row가 cell을 직접 소유하도록 한다.
|
|
18
|
+
jsx(DatagridRowWrapper, {
|
|
19
|
+
role: role ?? 'row',
|
|
12
20
|
...props,
|
|
13
21
|
children: /*#__PURE__*/jsx(BaseDatagridRow, {
|
|
22
|
+
role: 'presentation',
|
|
14
23
|
wrap: false,
|
|
15
24
|
gapX: gapX,
|
|
16
25
|
alignItems: alignItems,
|
|
17
26
|
justifyContent: justifyContent,
|
|
18
|
-
children: children
|
|
27
|
+
children: columnHeader ? Children.map(children, child => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
|
|
28
|
+
columnHeader: child.props.columnHeader ?? true
|
|
29
|
+
}) : child) : children
|
|
19
30
|
})
|
|
20
31
|
});
|
|
21
32
|
const BaseDatagridRow = Grid;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isNullable } from '@teamturing/utils';
|
|
2
|
+
import ItemList from '../ItemList/index.js';
|
|
3
|
+
import DatagridCell from './DatagridCell.js';
|
|
4
|
+
import DatagridRow from './DatagridRow.js';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
const DatagridRowList = ({
|
|
8
|
+
rows,
|
|
9
|
+
columns,
|
|
10
|
+
rowProps = {
|
|
11
|
+
alignItems: 'center'
|
|
12
|
+
},
|
|
13
|
+
renderItemWrapper,
|
|
14
|
+
emptyState,
|
|
15
|
+
columnsTransformer = columns => columns
|
|
16
|
+
}) => /*#__PURE__*/jsx(ItemList, {
|
|
17
|
+
items: rows,
|
|
18
|
+
renderItem: (row, i) => /*#__PURE__*/jsx(DatagridRow, {
|
|
19
|
+
...rowProps,
|
|
20
|
+
children: columnsTransformer(columns).filter(column => !isNullable(column)).map(({
|
|
21
|
+
field,
|
|
22
|
+
renderValue,
|
|
23
|
+
size
|
|
24
|
+
}) => /*#__PURE__*/jsx(DatagridCell, {
|
|
25
|
+
size: size,
|
|
26
|
+
children: renderValue(row, i)
|
|
27
|
+
}, field))
|
|
28
|
+
}, row.id),
|
|
29
|
+
renderItemWrapper: renderItemWrapper,
|
|
30
|
+
emptyState: emptyState
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export { DatagridRowList as default };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { forcePixelValue } from '@teamturing/utils';
|
|
2
|
+
import { useId, isValidElement, cloneElement } from 'react';
|
|
2
3
|
import styled from 'styled-components';
|
|
3
4
|
import useRelocation from '../../hook/useRelocation.js';
|
|
4
5
|
import { sx } from '../../utils/styled-system/index.js';
|
|
@@ -6,12 +7,16 @@ import DatagridBody from './DatagridBody.js';
|
|
|
6
7
|
import DatagridCell from './DatagridCell.js';
|
|
7
8
|
import DatagridHeader from './DatagridHeader.js';
|
|
8
9
|
import DatagridRow from './DatagridRow.js';
|
|
10
|
+
import DatagridRowList from './DatagridRowList.js';
|
|
9
11
|
import DatagridSubheader from './DatagridSubheader.js';
|
|
10
12
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
11
13
|
|
|
12
14
|
const Datagrid = ({
|
|
13
15
|
children,
|
|
14
16
|
sx,
|
|
17
|
+
role = 'table',
|
|
18
|
+
'aria-label': ariaLabel,
|
|
19
|
+
'aria-labelledby': ariaLabelledby,
|
|
15
20
|
...props
|
|
16
21
|
}) => {
|
|
17
22
|
const [relocatableComponentsObject, restConmponents] = useRelocation({
|
|
@@ -21,9 +26,28 @@ const Datagrid = ({
|
|
|
21
26
|
subHeader: DatagridSubheader
|
|
22
27
|
}
|
|
23
28
|
});
|
|
29
|
+
|
|
30
|
+
// Header가 있고 별도 라벨이 지정되지 않은 경우, Header를 표의 접근 가능한 이름으로 연결한다.
|
|
31
|
+
const generatedHeaderId = useId();
|
|
32
|
+
const {
|
|
33
|
+
header: rawHeader,
|
|
34
|
+
subHeader
|
|
35
|
+
} = relocatableComponentsObject;
|
|
36
|
+
let headerNode = rawHeader;
|
|
37
|
+
let resolvedLabelledby = ariaLabelledby;
|
|
38
|
+
if (!ariaLabel && !ariaLabelledby && /*#__PURE__*/isValidElement(rawHeader)) {
|
|
39
|
+
const headerId = rawHeader.props.id ?? generatedHeaderId;
|
|
40
|
+
headerNode = /*#__PURE__*/cloneElement(rawHeader, {
|
|
41
|
+
id: headerId
|
|
42
|
+
});
|
|
43
|
+
resolvedLabelledby = headerId;
|
|
44
|
+
}
|
|
24
45
|
return /*#__PURE__*/jsxs(DatagridWrapper, {
|
|
25
46
|
sx: sx,
|
|
26
|
-
children: [
|
|
47
|
+
children: [headerNode, subHeader, /*#__PURE__*/jsx(BaseDatagrid, {
|
|
48
|
+
role: role,
|
|
49
|
+
"aria-label": ariaLabel,
|
|
50
|
+
"aria-labelledby": resolvedLabelledby,
|
|
27
51
|
...props,
|
|
28
52
|
children: restConmponents
|
|
29
53
|
})]
|
|
@@ -46,6 +70,7 @@ var index = Object.assign(Datagrid, {
|
|
|
46
70
|
Subheader: DatagridSubheader,
|
|
47
71
|
Body: DatagridBody,
|
|
48
72
|
Row: DatagridRow,
|
|
73
|
+
RowList: DatagridRowList,
|
|
49
74
|
Cell: DatagridCell
|
|
50
75
|
});
|
|
51
76
|
|
|
@@ -40,6 +40,7 @@ const DescriptionList = ({
|
|
|
40
40
|
children: [/*#__PURE__*/jsx(Grid.Unit, {
|
|
41
41
|
size: titleUnitSize,
|
|
42
42
|
children: /*#__PURE__*/jsxs(View, {
|
|
43
|
+
role: 'term',
|
|
43
44
|
display: 'flex',
|
|
44
45
|
alignItems: 'center',
|
|
45
46
|
flexWrap: 'nowrap',
|
|
@@ -54,7 +55,10 @@ const DescriptionList = ({
|
|
|
54
55
|
})
|
|
55
56
|
}), /*#__PURE__*/jsx(Grid.Unit, {
|
|
56
57
|
size: descriptionUnitSize,
|
|
57
|
-
children:
|
|
58
|
+
children: /*#__PURE__*/jsx(View, {
|
|
59
|
+
role: 'definition',
|
|
60
|
+
children: renderDescription(!isNullable(description) ? description : '-')
|
|
61
|
+
})
|
|
58
62
|
})]
|
|
59
63
|
});
|
|
60
64
|
},
|
package/esm/core/Dialog/index.js
CHANGED
package/esm/core/Drawer/index.js
CHANGED
|
@@ -27,6 +27,8 @@ const Drawer = ({
|
|
|
27
27
|
size = 'm',
|
|
28
28
|
direction = 'right',
|
|
29
29
|
initialFocusRef,
|
|
30
|
+
'aria-label': ariaLabel,
|
|
31
|
+
'aria-labelledby': ariaLabelledby,
|
|
30
32
|
sx
|
|
31
33
|
}, ref) => {
|
|
32
34
|
const theme = useTheme();
|
|
@@ -51,7 +53,8 @@ const Drawer = ({
|
|
|
51
53
|
useFocusTrap({
|
|
52
54
|
containerRef: drawerRef,
|
|
53
55
|
initialFocusRef: initialFocusRef || closeButtonRef,
|
|
54
|
-
disabled: !isOpen
|
|
56
|
+
disabled: !isOpen,
|
|
57
|
+
restoreFocusOnCleanUp: true
|
|
55
58
|
});
|
|
56
59
|
useEffect(() => {
|
|
57
60
|
if (isOpen && isOutsideClickDismissable) {
|
|
@@ -134,6 +137,8 @@ const Drawer = ({
|
|
|
134
137
|
className: `trk-drawer--${size} trk-drawer--${direction}`,
|
|
135
138
|
ref: drawerRef,
|
|
136
139
|
"aria-modal": 'true',
|
|
140
|
+
"aria-label": ariaLabel,
|
|
141
|
+
"aria-labelledby": ariaLabelledby,
|
|
137
142
|
role: 'dialog',
|
|
138
143
|
tabIndex: -1,
|
|
139
144
|
size: size,
|
|
@@ -152,6 +157,7 @@ const Drawer = ({
|
|
|
152
157
|
icon: CloseIcon,
|
|
153
158
|
variant: 'plain-bold',
|
|
154
159
|
size: 'm',
|
|
160
|
+
"aria-label": theme.locales?.Drawer?.closeButtonLabel ?? '닫기',
|
|
155
161
|
onClick: handleDismiss
|
|
156
162
|
})
|
|
157
163
|
}), children]
|
package/esm/core/Flash/index.js
CHANGED
|
@@ -20,9 +20,12 @@ const Flash = ({
|
|
|
20
20
|
return /*#__PURE__*/jsxs(BaseFlash, {
|
|
21
21
|
ref: ref,
|
|
22
22
|
variant: variant,
|
|
23
|
+
role: variant === 'danger' ? 'alert' : 'status',
|
|
24
|
+
"aria-live": variant === 'danger' ? 'assertive' : 'polite',
|
|
23
25
|
...props,
|
|
24
26
|
children: [/*#__PURE__*/jsx(Icon, {
|
|
25
|
-
className: 'flash__leading_icon'
|
|
27
|
+
className: 'flash__leading_icon',
|
|
28
|
+
"aria-hidden": true
|
|
26
29
|
}), /*#__PURE__*/jsxs("div", {
|
|
27
30
|
className: 'flash__content',
|
|
28
31
|
children: [/*#__PURE__*/jsx("span", {
|
|
@@ -7,11 +7,11 @@ const FormControlCaption = ({
|
|
|
7
7
|
children
|
|
8
8
|
}) => {
|
|
9
9
|
const {
|
|
10
|
-
|
|
10
|
+
captionId
|
|
11
11
|
} = useContext(FormControlContext);
|
|
12
12
|
return /*#__PURE__*/jsx(Text, {
|
|
13
13
|
as: 'span',
|
|
14
|
-
id:
|
|
14
|
+
id: captionId,
|
|
15
15
|
typography: 'xxs',
|
|
16
16
|
color: 'text/neutral/subtlest',
|
|
17
17
|
children: children
|
|
@@ -8,10 +8,11 @@ const FormControlErrorMessage = ({
|
|
|
8
8
|
children
|
|
9
9
|
}) => {
|
|
10
10
|
const {
|
|
11
|
-
|
|
11
|
+
errorId
|
|
12
12
|
} = useContext(FormControlContext);
|
|
13
13
|
return /*#__PURE__*/jsx(StyledText, {
|
|
14
|
-
id:
|
|
14
|
+
id: errorId,
|
|
15
|
+
role: 'alert',
|
|
15
16
|
typography: 'xxs',
|
|
16
17
|
color: 'text/danger',
|
|
17
18
|
children: children
|
|
@@ -8,10 +8,11 @@ const FormControlSuccessMessage = ({
|
|
|
8
8
|
children
|
|
9
9
|
}) => {
|
|
10
10
|
const {
|
|
11
|
-
|
|
11
|
+
successId
|
|
12
12
|
} = useContext(FormControlContext);
|
|
13
13
|
return /*#__PURE__*/jsx(StyledText, {
|
|
14
|
-
id:
|
|
14
|
+
id: successId,
|
|
15
|
+
role: 'status',
|
|
15
16
|
typography: 'xxs',
|
|
16
17
|
color: 'text/success',
|
|
17
18
|
children: children
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef, createContext, isValidElement, cloneElement } from 'react';
|
|
1
|
+
import { forwardRef, createContext, isValidElement, useId, cloneElement } from 'react';
|
|
2
2
|
import useRelocation from '../../hook/useRelocation.js';
|
|
3
3
|
import Checkbox from '../Checkbox/index.js';
|
|
4
4
|
import Radio from '../Radio/index.js';
|
|
@@ -38,11 +38,35 @@ const FormControl = ({
|
|
|
38
38
|
const inputComponentCandidates = [TextInput, Textarea, Select, SearchSelectInput, Checkbox, Radio, Switch, ...additionalInputComponentCandidates];
|
|
39
39
|
const InputComponent = restComponents.find(component => inputComponentCandidates.some(candidate => /*#__PURE__*/isValidElement(component) && component.type === candidate));
|
|
40
40
|
const isHorizontalLayoutNeeded = /*#__PURE__*/isValidElement(InputComponent) && (InputComponent.type === Checkbox || InputComponent.type === Radio || InputComponent.type === Switch);
|
|
41
|
+
const reactId = useId();
|
|
42
|
+
const resolvedId = id ?? reactId;
|
|
43
|
+
const captionId = `${resolvedId}-caption`;
|
|
44
|
+
const errorId = `${resolvedId}-error`;
|
|
45
|
+
const successId = `${resolvedId}-success`;
|
|
46
|
+
const hasCaption = Boolean(relocatableComponentsObject.caption);
|
|
47
|
+
const hasError = Boolean(relocatableComponentsObject.errorMessage);
|
|
48
|
+
const hasSuccess = Boolean(relocatableComponentsObject.successMessage);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 소비자가 Input에 직접 전달한 aria 값을 보존하기 위해 기존 props와 병합합니다.
|
|
52
|
+
*/
|
|
53
|
+
const inputProps = /*#__PURE__*/isValidElement(InputComponent) ? InputComponent.props : {};
|
|
54
|
+
const describedBy = [inputProps['aria-describedby'], hasCaption && captionId, hasError && errorId, hasSuccess && successId].filter(Boolean).join(' ') || undefined;
|
|
55
|
+
const inputA11yProps = {
|
|
56
|
+
'id': resolvedId,
|
|
57
|
+
disabled,
|
|
58
|
+
'aria-invalid': hasError ? true : inputProps['aria-invalid'],
|
|
59
|
+
'aria-required': required ? true : inputProps['aria-required'],
|
|
60
|
+
'aria-describedby': describedBy
|
|
61
|
+
};
|
|
41
62
|
return /*#__PURE__*/jsx(FormControlContext.Provider, {
|
|
42
63
|
value: {
|
|
43
|
-
id,
|
|
64
|
+
id: resolvedId,
|
|
44
65
|
disabled,
|
|
45
|
-
required
|
|
66
|
+
required,
|
|
67
|
+
captionId,
|
|
68
|
+
errorId,
|
|
69
|
+
successId
|
|
46
70
|
},
|
|
47
71
|
children: isHorizontalLayoutNeeded ? /*#__PURE__*/jsxs(View, {
|
|
48
72
|
ref: ref,
|
|
@@ -54,10 +78,7 @@ const FormControl = ({
|
|
|
54
78
|
...props,
|
|
55
79
|
children: [/*#__PURE__*/jsx(View, {
|
|
56
80
|
display: 'inline-flex',
|
|
57
|
-
children: /*#__PURE__*/cloneElement(InputComponent,
|
|
58
|
-
id,
|
|
59
|
-
disabled
|
|
60
|
-
})
|
|
81
|
+
children: /*#__PURE__*/cloneElement(InputComponent, inputA11yProps)
|
|
61
82
|
}), /*#__PURE__*/jsxs(View, {
|
|
62
83
|
sx: {
|
|
63
84
|
'& > span': {
|
|
@@ -96,10 +117,7 @@ const FormControl = ({
|
|
|
96
117
|
columnGap: 1
|
|
97
118
|
},
|
|
98
119
|
children: [relocatableComponentsObject.label, relocatableComponentsObject.tooltipIcon]
|
|
99
|
-
}), /*#__PURE__*/cloneElement(InputComponent,
|
|
100
|
-
id,
|
|
101
|
-
disabled
|
|
102
|
-
}), relocatableComponentsObject.caption, relocatableComponentsObject.errorMessage, relocatableComponentsObject.successMessage]
|
|
120
|
+
}), /*#__PURE__*/cloneElement(InputComponent, inputA11yProps), relocatableComponentsObject.caption, relocatableComponentsObject.errorMessage, relocatableComponentsObject.successMessage]
|
|
103
121
|
})
|
|
104
122
|
});
|
|
105
123
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef } from 'react';
|
|
1
|
+
import { forwardRef, useEffect } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import '../../node_modules/styled-system/dist/index.esm.js';
|
|
4
4
|
import Spinner from '../Spinner/index.js';
|
|
@@ -14,6 +14,13 @@ const IconButton = /*#__PURE__*/forwardRef(({
|
|
|
14
14
|
loading = false,
|
|
15
15
|
...props
|
|
16
16
|
}, ref) => {
|
|
17
|
+
const hasAccessibleName = Boolean(props['aria-label'] || props['aria-labelledby'] || props['title']);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (process.env.NODE_ENV !== 'production' && !hasAccessibleName) {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.warn('IconButton: An icon-only button should provide an accessible name via `aria-label`, `aria-labelledby`, or `title` so that assistive technologies can identify it.');
|
|
22
|
+
}
|
|
23
|
+
}, [hasAccessibleName]);
|
|
17
24
|
return /*#__PURE__*/jsx(BaseIconButton, {
|
|
18
25
|
ref: ref,
|
|
19
26
|
icon: Icon,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef } from 'react';
|
|
1
|
+
import { forwardRef, useEffect } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import '../../node_modules/styled-system/dist/index.esm.js';
|
|
4
4
|
import { sx } from '../../utils/styled-system/index.js';
|
|
@@ -15,20 +15,30 @@ const IconToggleButton = ({
|
|
|
15
15
|
disabled = false,
|
|
16
16
|
sx,
|
|
17
17
|
...props
|
|
18
|
-
}, ref) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
}, ref) => {
|
|
19
|
+
const hasAccessibleName = Boolean(props['aria-label'] || props['aria-labelledby'] || props['title']);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (process.env.NODE_ENV !== 'production' && !hasAccessibleName) {
|
|
22
|
+
// eslint-disable-next-line no-console
|
|
23
|
+
console.warn('IconToggleButton: An icon-only button should provide an accessible name via `aria-label`, `aria-labelledby`, or `title` so that assistive technologies can identify it.');
|
|
24
|
+
}
|
|
25
|
+
}, [hasAccessibleName]);
|
|
26
|
+
return /*#__PURE__*/jsx(BaseIconToggleButton, {
|
|
27
|
+
ref: ref,
|
|
28
|
+
icon: Icon,
|
|
29
|
+
size: size,
|
|
30
|
+
shape: shape,
|
|
31
|
+
variant: variant,
|
|
32
|
+
selected: selected,
|
|
33
|
+
"aria-pressed": selected,
|
|
34
|
+
type: 'button',
|
|
35
|
+
disabled: disabled,
|
|
36
|
+
$disabled: disabled,
|
|
37
|
+
sx: sx,
|
|
38
|
+
...props,
|
|
39
|
+
children: /*#__PURE__*/jsx(Icon, {})
|
|
40
|
+
});
|
|
41
|
+
};
|
|
32
42
|
const BaseIconToggleButton = /*#__PURE__*/styled(UnstyledButton).withConfig({
|
|
33
43
|
displayName: "IconToggleButton__BaseIconToggleButton",
|
|
34
44
|
componentId: "sc-1y68w0-0"
|
|
@@ -71,10 +71,10 @@ const Overlay = ({
|
|
|
71
71
|
}, [isOpen, handleOutsideClick]);
|
|
72
72
|
return isOpen ? /*#__PURE__*/jsx(BaseOverlay, {
|
|
73
73
|
ref: overlayRef,
|
|
74
|
+
role: 'dialog',
|
|
74
75
|
size: size,
|
|
75
76
|
maxHeight: maxHeight,
|
|
76
77
|
...props,
|
|
77
|
-
role: 'dialog',
|
|
78
78
|
children: children
|
|
79
79
|
}) : null;
|
|
80
80
|
};
|
|
@@ -163,7 +163,9 @@ const SearchSelectInput = ({
|
|
|
163
163
|
}, overlayHandler)
|
|
164
164
|
})]
|
|
165
165
|
}),
|
|
166
|
-
children: popperProps
|
|
166
|
+
children: (popperProps, {
|
|
167
|
+
isOpen
|
|
168
|
+
}) => /*#__PURE__*/jsxs(TextInputWrapper, {
|
|
167
169
|
...(disabled ? {} : {
|
|
168
170
|
...popperProps,
|
|
169
171
|
onClick: e => {
|
|
@@ -214,6 +216,9 @@ const SearchSelectInput = ({
|
|
|
214
216
|
}) : null, /*#__PURE__*/jsx(BaseInput, {
|
|
215
217
|
id: id,
|
|
216
218
|
ref: labelInputRef,
|
|
219
|
+
role: 'combobox',
|
|
220
|
+
"aria-haspopup": 'listbox',
|
|
221
|
+
"aria-expanded": isOpen,
|
|
217
222
|
readOnly: true,
|
|
218
223
|
onChange: noop,
|
|
219
224
|
autoComplete: 'off',
|
|
@@ -27,6 +27,7 @@ const Spinner = /*#__PURE__*/forwardRef(({
|
|
|
27
27
|
variant: propsVariant,
|
|
28
28
|
width = 32,
|
|
29
29
|
height = 32,
|
|
30
|
+
label = '로딩 중',
|
|
30
31
|
...props
|
|
31
32
|
}, ref) => {
|
|
32
33
|
const theme = useTheme();
|
|
@@ -35,10 +36,18 @@ const Spinner = /*#__PURE__*/forwardRef(({
|
|
|
35
36
|
'progress-gradient': ProgressGradientSpinner,
|
|
36
37
|
'progress-line': ProgressLineSpinner
|
|
37
38
|
}[variant];
|
|
39
|
+
const a11yProps = label ? {
|
|
40
|
+
'role': 'status',
|
|
41
|
+
'aria-label': label,
|
|
42
|
+
'aria-busy': true
|
|
43
|
+
} : {
|
|
44
|
+
'aria-hidden': true
|
|
45
|
+
};
|
|
38
46
|
return /*#__PURE__*/jsx(SpinnerComponent, {
|
|
39
47
|
ref: ref,
|
|
40
48
|
width: width,
|
|
41
49
|
height: height,
|
|
50
|
+
...a11yProps,
|
|
42
51
|
...props
|
|
43
52
|
});
|
|
44
53
|
});
|
|
@@ -3,24 +3,40 @@ import View from '../View/index.js';
|
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
const StyledIcon = /*#__PURE__*/forwardRef(({
|
|
6
|
-
icon: Icon,
|
|
6
|
+
'icon': Icon,
|
|
7
7
|
sx,
|
|
8
8
|
className,
|
|
9
|
+
'aria-label': ariaLabel,
|
|
10
|
+
'aria-hidden': ariaHidden,
|
|
9
11
|
...props
|
|
10
|
-
}, ref) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
}, ref) => {
|
|
13
|
+
/**
|
|
14
|
+
* 기본적으로 장식용 아이콘으로 간주해 `aria-hidden`을 부여합니다.
|
|
15
|
+
* 의미 있는 아이콘이라면 `aria-label`을 전달하세요. (`role="img"`로 노출됩니다.)
|
|
16
|
+
*/
|
|
17
|
+
const a11yProps = ariaLabel ? {
|
|
18
|
+
'role': 'img',
|
|
19
|
+
'aria-label': ariaLabel,
|
|
20
|
+
'aria-hidden': ariaHidden
|
|
21
|
+
} : {
|
|
22
|
+
'aria-hidden': ariaHidden ?? true
|
|
23
|
+
};
|
|
24
|
+
return /*#__PURE__*/jsx(View, {
|
|
25
|
+
ref: ref,
|
|
26
|
+
...props,
|
|
27
|
+
...a11yProps,
|
|
28
|
+
className: `trk-styled_icon__wrapper ${className}`,
|
|
29
|
+
color: props.color,
|
|
30
|
+
sx: {
|
|
31
|
+
'& svg': {
|
|
32
|
+
display: 'inline-flex',
|
|
33
|
+
width: '100%',
|
|
34
|
+
height: '100%'
|
|
35
|
+
},
|
|
36
|
+
...sx
|
|
20
37
|
},
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}));
|
|
38
|
+
children: /*#__PURE__*/jsx(Icon, {})
|
|
39
|
+
});
|
|
40
|
+
});
|
|
25
41
|
|
|
26
42
|
export { StyledIcon as default };
|
package/esm/core/Switch/index.js
CHANGED
package/esm/core/Tab/TabItem.js
CHANGED
package/esm/core/Tab/index.js
CHANGED
|
@@ -108,6 +108,7 @@ const Tab = ({
|
|
|
108
108
|
size: 's',
|
|
109
109
|
variant: 'plain-bold',
|
|
110
110
|
icon: ChevronLeftIcon,
|
|
111
|
+
"aria-label": theme.locales?.Tab?.scrollLeftLabel ?? '왼쪽으로 스크롤',
|
|
111
112
|
onClick: handleLeftButtonClick
|
|
112
113
|
})
|
|
113
114
|
})]
|
|
@@ -139,6 +140,7 @@ const Tab = ({
|
|
|
139
140
|
size: 's',
|
|
140
141
|
variant: 'plain-bold',
|
|
141
142
|
icon: ChevronRightIcon,
|
|
143
|
+
"aria-label": theme.locales?.Tab?.scrollRightLabel ?? '오른쪽으로 스크롤',
|
|
142
144
|
onClick: handleRightButtonClick
|
|
143
145
|
})
|
|
144
146
|
})]
|
package/esm/core/Toast/index.js
CHANGED
|
@@ -8,13 +8,18 @@ const Toast = ({
|
|
|
8
8
|
variant = 'success',
|
|
9
9
|
icon: Icon = variant === 'success' ? CheckInCircleIcon : ExclamationPointInCircleIcon,
|
|
10
10
|
resizing = 'hug',
|
|
11
|
-
children
|
|
11
|
+
children,
|
|
12
|
+
...props
|
|
12
13
|
}) => {
|
|
13
14
|
return /*#__PURE__*/jsxs(BaseToast, {
|
|
14
15
|
variant: variant,
|
|
15
16
|
resizing: resizing,
|
|
17
|
+
role: variant === 'warning' ? 'alert' : 'status',
|
|
18
|
+
"aria-live": variant === 'warning' ? 'assertive' : 'polite',
|
|
19
|
+
...props,
|
|
16
20
|
children: [/*#__PURE__*/jsx(Icon, {
|
|
17
|
-
className: 'toast__leading_icon'
|
|
21
|
+
className: 'toast__leading_icon',
|
|
22
|
+
"aria-hidden": true
|
|
18
23
|
}), children]
|
|
19
24
|
});
|
|
20
25
|
};
|
package/esm/theme/index.js
CHANGED
|
@@ -30,6 +30,16 @@ const theme = {
|
|
|
30
30
|
UploadInput: {
|
|
31
31
|
placeholder: '파일을 끌어다 놓으세요',
|
|
32
32
|
selectFile: '파일 선택'
|
|
33
|
+
},
|
|
34
|
+
Dialog: {
|
|
35
|
+
closeButtonLabel: '닫기'
|
|
36
|
+
},
|
|
37
|
+
Drawer: {
|
|
38
|
+
closeButtonLabel: '닫기'
|
|
39
|
+
},
|
|
40
|
+
Tab: {
|
|
41
|
+
scrollLeftLabel: '왼쪽으로 스크롤',
|
|
42
|
+
scrollRightLabel: '오른쪽으로 스크롤'
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamturing/react-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.76.0",
|
|
4
4
|
"description": "React components, hooks for create teamturing web application",
|
|
5
5
|
"author": "Sungchang Park <psch300@gmail.com> (https://github.com/psch300)",
|
|
6
6
|
"homepage": "https://github.com/weareteamturing/bombe#readme",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"react-textarea-autosize": "^8.5.3",
|
|
66
66
|
"styled-system": "^5.1.5"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "786f4e6ff9bd2f7324b536c7872cf06eb4b0b3b6"
|
|
69
69
|
}
|