@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.
Files changed (41) hide show
  1. package/dist/core/Datagrid/DatagridBody.d.ts +3 -3
  2. package/dist/core/Datagrid/DatagridCell.d.ts +8 -3
  3. package/dist/core/Datagrid/DatagridHeader.d.ts +2 -2
  4. package/dist/core/Datagrid/DatagridRow.d.ts +8 -3
  5. package/dist/core/Datagrid/DatagridRowList.d.ts +21 -0
  6. package/dist/core/Datagrid/index.d.ts +9 -5
  7. package/dist/core/Drawer/index.d.ts +3 -3
  8. package/dist/core/FormControl/index.d.ts +9 -2
  9. package/dist/core/Grid/index.d.ts +4 -4
  10. package/dist/core/Spinner/index.d.ts +12 -0
  11. package/dist/core/StyledIcon/index.d.ts +2 -2
  12. package/dist/core/Toast/index.d.ts +3 -3
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +251 -77
  15. package/dist/theme/index.d.ts +30 -0
  16. package/esm/core/ClickArea/index.js +20 -10
  17. package/esm/core/Datagrid/DatagridBody.js +2 -0
  18. package/esm/core/Datagrid/DatagridCell.js +3 -0
  19. package/esm/core/Datagrid/DatagridRow.js +13 -2
  20. package/esm/core/Datagrid/DatagridRowList.js +33 -0
  21. package/esm/core/Datagrid/index.js +26 -1
  22. package/esm/core/DescriptionList/index.js +5 -1
  23. package/esm/core/Dialog/index.js +1 -0
  24. package/esm/core/Drawer/index.js +7 -1
  25. package/esm/core/Flash/index.js +4 -1
  26. package/esm/core/FormControl/FormControlCaption.js +2 -2
  27. package/esm/core/FormControl/FormControlErrorMessage.js +3 -2
  28. package/esm/core/FormControl/FormControlSuccessMessage.js +3 -2
  29. package/esm/core/FormControl/index.js +29 -11
  30. package/esm/core/IconButton/index.js +8 -1
  31. package/esm/core/IconToggleButton/index.js +25 -15
  32. package/esm/core/Overlay/index.js +1 -1
  33. package/esm/core/SearchSelectInput/index.js +6 -1
  34. package/esm/core/Spinner/index.js +9 -0
  35. package/esm/core/StyledIcon/index.js +31 -15
  36. package/esm/core/Switch/index.js +1 -0
  37. package/esm/core/Tab/TabItem.js +1 -0
  38. package/esm/core/Tab/index.js +2 -0
  39. package/esm/core/Toast/index.js +7 -2
  40. package/esm/theme/index.js +10 -0
  41. 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
- }) => /*#__PURE__*/jsx(DatagridRowWrapper, {
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: [relocatableComponentsObject.header, relocatableComponentsObject.subHeader, /*#__PURE__*/jsx(BaseDatagrid, {
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: renderDescription(!isNullable(description) ? description : '-')
58
+ children: /*#__PURE__*/jsx(View, {
59
+ role: 'definition',
60
+ children: renderDescription(!isNullable(description) ? description : '-')
61
+ })
58
62
  })]
59
63
  });
60
64
  },
@@ -191,6 +191,7 @@ const Dialog = ({
191
191
  icon: CloseIcon,
192
192
  variant: 'plain-bold',
193
193
  size: 'm',
194
+ "aria-label": theme.locales?.Dialog?.closeButtonLabel ?? '닫기',
194
195
  onClick: handleDismiss
195
196
  })
196
197
  }), /*#__PURE__*/jsx(DialogContext.Provider, {
@@ -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]
@@ -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
- id
10
+ captionId
11
11
  } = useContext(FormControlContext);
12
12
  return /*#__PURE__*/jsx(Text, {
13
13
  as: 'span',
14
- id: 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
- id
11
+ errorId
12
12
  } = useContext(FormControlContext);
13
13
  return /*#__PURE__*/jsx(StyledText, {
14
- id: 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
- id
11
+ successId
12
12
  } = useContext(FormControlContext);
13
13
  return /*#__PURE__*/jsx(StyledText, {
14
- id: 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) => /*#__PURE__*/jsx(BaseIconToggleButton, {
19
- ref: ref,
20
- icon: Icon,
21
- size: size,
22
- shape: shape,
23
- variant: variant,
24
- selected: selected,
25
- type: 'button',
26
- disabled: disabled,
27
- $disabled: disabled,
28
- sx: sx,
29
- ...props,
30
- children: /*#__PURE__*/jsx(Icon, {})
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 => /*#__PURE__*/jsxs(TextInputWrapper, {
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) => /*#__PURE__*/jsx(View, {
11
- ref: ref,
12
- ...props,
13
- className: `trk-styled_icon__wrapper ${className}`,
14
- color: props.color,
15
- sx: {
16
- '& svg': {
17
- display: 'inline-flex',
18
- width: '100%',
19
- height: '100%'
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
- ...sx
22
- },
23
- children: /*#__PURE__*/jsx(Icon, {})
24
- }));
38
+ children: /*#__PURE__*/jsx(Icon, {})
39
+ });
40
+ });
25
41
 
26
42
  export { StyledIcon as default };
@@ -13,6 +13,7 @@ const Switch = ({
13
13
  const checkboxRef = useProvidedOrCreatedRef(ref);
14
14
  return /*#__PURE__*/jsx(BaseSwitch, {
15
15
  ref: checkboxRef,
16
+ role: 'switch',
16
17
  checked: checked,
17
18
  validationStatus: validationStatus,
18
19
  ...props
@@ -41,6 +41,7 @@ const TabItem = ({
41
41
  className: 'trk-tab_item',
42
42
  type: 'button',
43
43
  role: 'tab',
44
+ "aria-selected": selected,
44
45
  ref: ref,
45
46
  variant: variant,
46
47
  size: size,
@@ -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
  })]
@@ -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
  };
@@ -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.74.0",
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": "b096bdb9ffa2f75b160b773e60a1a2130989ff3c"
68
+ "gitHead": "786f4e6ff9bd2f7324b536c7872cf06eb4b0b3b6"
69
69
  }