@teamturing/react-kit 2.75.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/Drawer/index.d.ts +3 -3
- package/dist/core/FormControl/index.d.ts +9 -2
- 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.js +172 -62
- package/dist/theme/index.d.ts +30 -0
- package/esm/core/ClickArea/index.js +20 -10
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropsWithChildren, RefObject } from 'react';
|
|
1
|
+
import { HTMLAttributes, PropsWithChildren, RefObject } from 'react';
|
|
2
2
|
import { SxProp } from '../../utils/styled-system';
|
|
3
3
|
import { DrawerBodyProps } from './DrawerBody';
|
|
4
4
|
import { DrawerFooterProps } from './DrawerFooter';
|
|
@@ -16,7 +16,7 @@ type Props = {
|
|
|
16
16
|
size?: DrawerSizeType;
|
|
17
17
|
direction?: DrawerDirectionType;
|
|
18
18
|
initialFocusRef?: RefObject<HTMLElement>;
|
|
19
|
-
} & SxProp;
|
|
19
|
+
} & Pick<HTMLAttributes<HTMLDivElement>, 'aria-label' | 'aria-labelledby'> & SxProp;
|
|
20
20
|
declare const _default: import("react").ForwardRefExoticComponent<{
|
|
21
21
|
isOpen?: boolean | undefined;
|
|
22
22
|
onDismiss?: (() => void) | undefined;
|
|
@@ -24,7 +24,7 @@ declare const _default: import("react").ForwardRefExoticComponent<{
|
|
|
24
24
|
size?: DrawerSizeType | undefined;
|
|
25
25
|
direction?: DrawerDirectionType | undefined;
|
|
26
26
|
initialFocusRef?: RefObject<HTMLElement> | undefined;
|
|
27
|
-
} & SxProp & {
|
|
27
|
+
} & Pick<HTMLAttributes<HTMLDivElement>, "aria-label" | "aria-labelledby"> & SxProp & {
|
|
28
28
|
children?: import("react").ReactNode;
|
|
29
29
|
} & import("react").RefAttributes<HTMLDivElement>> & {
|
|
30
30
|
UnstyledHeader: import("styled-components").IStyledComponent<"web", {
|
|
@@ -30,8 +30,15 @@ type FormControlFieldProps = {
|
|
|
30
30
|
label: string;
|
|
31
31
|
caption?: string;
|
|
32
32
|
};
|
|
33
|
-
type FormControlContextValue = {
|
|
34
|
-
|
|
33
|
+
type FormControlContextValue = {
|
|
34
|
+
/**
|
|
35
|
+
* `Caption`, `ErrorMessage`, `SuccessMessage`가 Input과 `aria-describedby`로 연결되기 위한 고유 ID입니다.
|
|
36
|
+
*/
|
|
37
|
+
captionId?: string;
|
|
38
|
+
errorId?: string;
|
|
39
|
+
successId?: string;
|
|
40
|
+
} & Omit<Props, 'additionalInputComponentCandidates'>;
|
|
41
|
+
declare const FormControlContext: import("react").Context<FormControlContextValue>;
|
|
35
42
|
declare const _default: import("react").ForwardRefExoticComponent<{
|
|
36
43
|
/**
|
|
37
44
|
* `FormControl`의 Input 요소를 컨트롤하기 위한 ID입니다. `Label`, `Caption`과 연결짓기 위해 사용합니다.
|
|
@@ -2,9 +2,21 @@ import { SVGProps } from 'react';
|
|
|
2
2
|
type SpinnerVariantType = 'progress-gradient' | 'progress-line';
|
|
3
3
|
type Props = {
|
|
4
4
|
variant?: SpinnerVariantType;
|
|
5
|
+
/**
|
|
6
|
+
* 스크린 리더에 읽힐 로딩 상태 텍스트를 정의합니다.
|
|
7
|
+
* 빈 문자열(`''`)을 전달하면 장식용으로 간주되어 보조 기술에서 숨겨집니다.
|
|
8
|
+
* @default '로딩 중'
|
|
9
|
+
*/
|
|
10
|
+
label?: string;
|
|
5
11
|
} & Omit<SVGProps<SVGSVGElement>, 'ref'>;
|
|
6
12
|
declare const Spinner: import("react").ForwardRefExoticComponent<{
|
|
7
13
|
variant?: SpinnerVariantType | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* 스크린 리더에 읽힐 로딩 상태 텍스트를 정의합니다.
|
|
16
|
+
* 빈 문자열(`''`)을 전달하면 장식용으로 간주되어 보조 기술에서 숨겨집니다.
|
|
17
|
+
* @default '로딩 중'
|
|
18
|
+
*/
|
|
19
|
+
label?: string | undefined;
|
|
8
20
|
} & Omit<SVGProps<SVGSVGElement>, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
|
|
9
21
|
export default Spinner;
|
|
10
22
|
export type { Props as SpinnerProps };
|
|
@@ -5,12 +5,12 @@ type Props = {
|
|
|
5
5
|
* @teamturing/icons와 함께 사용
|
|
6
6
|
*/
|
|
7
7
|
icon: ComponentType<SVGProps<SVGSVGElement>>;
|
|
8
|
-
} & Pick<ViewProps, 'size' | 'color' | 'sx'> & Pick<HTMLAttributes<HTMLDivElement>, 'className'>;
|
|
8
|
+
} & Pick<ViewProps, 'size' | 'color' | 'sx'> & Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'aria-label' | 'aria-hidden'>;
|
|
9
9
|
declare const StyledIcon: import("react").ForwardRefExoticComponent<{
|
|
10
10
|
/**
|
|
11
11
|
* @teamturing/icons와 함께 사용
|
|
12
12
|
*/
|
|
13
13
|
icon: ComponentType<SVGProps<SVGSVGElement>>;
|
|
14
|
-
} & Pick<ViewProps, "color" | "size" | "sx"> & Pick<HTMLAttributes<HTMLDivElement>, "className"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
14
|
+
} & Pick<ViewProps, "color" | "size" | "sx"> & Pick<HTMLAttributes<HTMLDivElement>, "className" | "aria-hidden" | "aria-label"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
15
|
export default StyledIcon;
|
|
16
16
|
export type { Props as StyledIconProps };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentType, PropsWithChildren, SVGProps } from 'react';
|
|
1
|
+
import { ComponentType, HTMLAttributes, PropsWithChildren, SVGProps } from 'react';
|
|
2
2
|
import { ResponsiveValue } from 'styled-system';
|
|
3
3
|
type ToastVariantType = 'success' | 'warning';
|
|
4
4
|
type Props = {
|
|
@@ -16,7 +16,7 @@ type Props = {
|
|
|
16
16
|
* 반응형 디자인이 적용됩니다.
|
|
17
17
|
*/
|
|
18
18
|
resizing?: ResponsiveValue<'hug' | 'fill'>;
|
|
19
|
-
}
|
|
20
|
-
declare const Toast: ({ variant, icon: Icon, resizing, children, }: PropsWithChildren<Props>) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
} & HTMLAttributes<HTMLDivElement>;
|
|
20
|
+
declare const Toast: ({ variant, icon: Icon, resizing, children, ...props }: PropsWithChildren<Props>) => import("react/jsx-runtime").JSX.Element;
|
|
21
21
|
export default Toast;
|
|
22
22
|
export type { Props as ToastProps };
|
package/dist/index.js
CHANGED
|
@@ -1916,10 +1916,10 @@ const Overlay = ({
|
|
|
1916
1916
|
}, [isOpen, handleOutsideClick]);
|
|
1917
1917
|
return isOpen ? /*#__PURE__*/jsxRuntime.jsx(BaseOverlay, {
|
|
1918
1918
|
ref: overlayRef,
|
|
1919
|
+
role: 'dialog',
|
|
1919
1920
|
size: size,
|
|
1920
1921
|
maxHeight: maxHeight,
|
|
1921
1922
|
...props,
|
|
1922
|
-
role: 'dialog',
|
|
1923
1923
|
children: children
|
|
1924
1924
|
}) : null;
|
|
1925
1925
|
};
|
|
@@ -2602,6 +2602,7 @@ const Spinner = /*#__PURE__*/React.forwardRef(({
|
|
|
2602
2602
|
variant: propsVariant,
|
|
2603
2603
|
width = 32,
|
|
2604
2604
|
height = 32,
|
|
2605
|
+
label = '로딩 중',
|
|
2605
2606
|
...props
|
|
2606
2607
|
}, ref) => {
|
|
2607
2608
|
const theme = styled.useTheme();
|
|
@@ -2610,10 +2611,18 @@ const Spinner = /*#__PURE__*/React.forwardRef(({
|
|
|
2610
2611
|
'progress-gradient': ProgressGradientSpinner,
|
|
2611
2612
|
'progress-line': ProgressLineSpinner
|
|
2612
2613
|
}[variant];
|
|
2614
|
+
const a11yProps = label ? {
|
|
2615
|
+
'role': 'status',
|
|
2616
|
+
'aria-label': label,
|
|
2617
|
+
'aria-busy': true
|
|
2618
|
+
} : {
|
|
2619
|
+
'aria-hidden': true
|
|
2620
|
+
};
|
|
2613
2621
|
return /*#__PURE__*/jsxRuntime.jsx(SpinnerComponent, {
|
|
2614
2622
|
ref: ref,
|
|
2615
2623
|
width: width,
|
|
2616
2624
|
height: height,
|
|
2625
|
+
...a11yProps,
|
|
2617
2626
|
...props
|
|
2618
2627
|
});
|
|
2619
2628
|
});
|
|
@@ -3277,25 +3286,41 @@ const BaseHorizontalDivider = /*#__PURE__*/styled__default.default.hr.withConfig
|
|
|
3277
3286
|
})(["display:block;margin:0;padding:0;border:none;width:100%;", " ", ""], border, sx);
|
|
3278
3287
|
|
|
3279
3288
|
const StyledIcon = /*#__PURE__*/React.forwardRef(({
|
|
3280
|
-
icon: Icon,
|
|
3289
|
+
'icon': Icon,
|
|
3281
3290
|
sx,
|
|
3282
3291
|
className,
|
|
3292
|
+
'aria-label': ariaLabel,
|
|
3293
|
+
'aria-hidden': ariaHidden,
|
|
3283
3294
|
...props
|
|
3284
|
-
}, ref) =>
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
'
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3295
|
+
}, ref) => {
|
|
3296
|
+
/**
|
|
3297
|
+
* 기본적으로 장식용 아이콘으로 간주해 `aria-hidden`을 부여합니다.
|
|
3298
|
+
* 의미 있는 아이콘이라면 `aria-label`을 전달하세요. (`role="img"`로 노출됩니다.)
|
|
3299
|
+
*/
|
|
3300
|
+
const a11yProps = ariaLabel ? {
|
|
3301
|
+
'role': 'img',
|
|
3302
|
+
'aria-label': ariaLabel,
|
|
3303
|
+
'aria-hidden': ariaHidden
|
|
3304
|
+
} : {
|
|
3305
|
+
'aria-hidden': ariaHidden ?? true
|
|
3306
|
+
};
|
|
3307
|
+
return /*#__PURE__*/jsxRuntime.jsx(View, {
|
|
3308
|
+
ref: ref,
|
|
3309
|
+
...props,
|
|
3310
|
+
...a11yProps,
|
|
3311
|
+
className: `trk-styled_icon__wrapper ${className}`,
|
|
3312
|
+
color: props.color,
|
|
3313
|
+
sx: {
|
|
3314
|
+
'& svg': {
|
|
3315
|
+
display: 'inline-flex',
|
|
3316
|
+
width: '100%',
|
|
3317
|
+
height: '100%'
|
|
3318
|
+
},
|
|
3319
|
+
...sx
|
|
3294
3320
|
},
|
|
3295
|
-
|
|
3296
|
-
}
|
|
3297
|
-
|
|
3298
|
-
}));
|
|
3321
|
+
children: /*#__PURE__*/jsxRuntime.jsx(Icon, {})
|
|
3322
|
+
});
|
|
3323
|
+
});
|
|
3299
3324
|
|
|
3300
3325
|
const TextInputTrailingAction = ({
|
|
3301
3326
|
icon: Icon,
|
|
@@ -3635,7 +3660,9 @@ const SearchSelectInput = ({
|
|
|
3635
3660
|
}, overlayHandler)
|
|
3636
3661
|
})]
|
|
3637
3662
|
}),
|
|
3638
|
-
children: popperProps
|
|
3663
|
+
children: (popperProps, {
|
|
3664
|
+
isOpen
|
|
3665
|
+
}) => /*#__PURE__*/jsxRuntime.jsxs(TextInputWrapper, {
|
|
3639
3666
|
...(disabled ? {} : {
|
|
3640
3667
|
...popperProps,
|
|
3641
3668
|
onClick: e => {
|
|
@@ -3686,6 +3713,9 @@ const SearchSelectInput = ({
|
|
|
3686
3713
|
}) : null, /*#__PURE__*/jsxRuntime.jsx(BaseInput, {
|
|
3687
3714
|
id: id,
|
|
3688
3715
|
ref: labelInputRef,
|
|
3716
|
+
role: 'combobox',
|
|
3717
|
+
"aria-haspopup": 'listbox',
|
|
3718
|
+
"aria-expanded": isOpen,
|
|
3689
3719
|
readOnly: true,
|
|
3690
3720
|
onChange: utils.noop,
|
|
3691
3721
|
autoComplete: 'off',
|
|
@@ -3973,6 +4003,7 @@ const Switch = ({
|
|
|
3973
4003
|
const checkboxRef = useProvidedOrCreatedRef(ref);
|
|
3974
4004
|
return /*#__PURE__*/jsxRuntime.jsx(BaseSwitch, {
|
|
3975
4005
|
ref: checkboxRef,
|
|
4006
|
+
role: 'switch',
|
|
3976
4007
|
checked: checked,
|
|
3977
4008
|
validationStatus: validationStatus,
|
|
3978
4009
|
...props
|
|
@@ -4140,11 +4171,11 @@ const FormControlCaption = ({
|
|
|
4140
4171
|
children
|
|
4141
4172
|
}) => {
|
|
4142
4173
|
const {
|
|
4143
|
-
|
|
4174
|
+
captionId
|
|
4144
4175
|
} = React.useContext(FormControlContext);
|
|
4145
4176
|
return /*#__PURE__*/jsxRuntime.jsx(Text, {
|
|
4146
4177
|
as: 'span',
|
|
4147
|
-
id:
|
|
4178
|
+
id: captionId,
|
|
4148
4179
|
typography: 'xxs',
|
|
4149
4180
|
color: 'text/neutral/subtlest',
|
|
4150
4181
|
children: children
|
|
@@ -4155,10 +4186,11 @@ const FormControlErrorMessage = ({
|
|
|
4155
4186
|
children
|
|
4156
4187
|
}) => {
|
|
4157
4188
|
const {
|
|
4158
|
-
|
|
4189
|
+
errorId
|
|
4159
4190
|
} = React.useContext(FormControlContext);
|
|
4160
4191
|
return /*#__PURE__*/jsxRuntime.jsx(StyledText$3, {
|
|
4161
|
-
id:
|
|
4192
|
+
id: errorId,
|
|
4193
|
+
role: 'alert',
|
|
4162
4194
|
typography: 'xxs',
|
|
4163
4195
|
color: 'text/danger',
|
|
4164
4196
|
children: children
|
|
@@ -4252,10 +4284,11 @@ const FormControlSuccessMessage = ({
|
|
|
4252
4284
|
children
|
|
4253
4285
|
}) => {
|
|
4254
4286
|
const {
|
|
4255
|
-
|
|
4287
|
+
successId
|
|
4256
4288
|
} = React.useContext(FormControlContext);
|
|
4257
4289
|
return /*#__PURE__*/jsxRuntime.jsx(StyledText$2, {
|
|
4258
|
-
id:
|
|
4290
|
+
id: successId,
|
|
4291
|
+
role: 'status',
|
|
4259
4292
|
typography: 'xxs',
|
|
4260
4293
|
color: 'text/success',
|
|
4261
4294
|
children: children
|
|
@@ -4318,11 +4351,35 @@ const FormControl = ({
|
|
|
4318
4351
|
const inputComponentCandidates = [TextInput$1, Textarea, Select$1, SearchSelectInput$1, Checkbox$1, Radio$1, Switch$1, ...additionalInputComponentCandidates];
|
|
4319
4352
|
const InputComponent = restComponents.find(component => inputComponentCandidates.some(candidate => /*#__PURE__*/React.isValidElement(component) && component.type === candidate));
|
|
4320
4353
|
const isHorizontalLayoutNeeded = /*#__PURE__*/React.isValidElement(InputComponent) && (InputComponent.type === Checkbox$1 || InputComponent.type === Radio$1 || InputComponent.type === Switch$1);
|
|
4354
|
+
const reactId = React.useId();
|
|
4355
|
+
const resolvedId = id ?? reactId;
|
|
4356
|
+
const captionId = `${resolvedId}-caption`;
|
|
4357
|
+
const errorId = `${resolvedId}-error`;
|
|
4358
|
+
const successId = `${resolvedId}-success`;
|
|
4359
|
+
const hasCaption = Boolean(relocatableComponentsObject.caption);
|
|
4360
|
+
const hasError = Boolean(relocatableComponentsObject.errorMessage);
|
|
4361
|
+
const hasSuccess = Boolean(relocatableComponentsObject.successMessage);
|
|
4362
|
+
|
|
4363
|
+
/**
|
|
4364
|
+
* 소비자가 Input에 직접 전달한 aria 값을 보존하기 위해 기존 props와 병합합니다.
|
|
4365
|
+
*/
|
|
4366
|
+
const inputProps = /*#__PURE__*/React.isValidElement(InputComponent) ? InputComponent.props : {};
|
|
4367
|
+
const describedBy = [inputProps['aria-describedby'], hasCaption && captionId, hasError && errorId, hasSuccess && successId].filter(Boolean).join(' ') || undefined;
|
|
4368
|
+
const inputA11yProps = {
|
|
4369
|
+
'id': resolvedId,
|
|
4370
|
+
disabled,
|
|
4371
|
+
'aria-invalid': hasError ? true : inputProps['aria-invalid'],
|
|
4372
|
+
'aria-required': required ? true : inputProps['aria-required'],
|
|
4373
|
+
'aria-describedby': describedBy
|
|
4374
|
+
};
|
|
4321
4375
|
return /*#__PURE__*/jsxRuntime.jsx(FormControlContext.Provider, {
|
|
4322
4376
|
value: {
|
|
4323
|
-
id,
|
|
4377
|
+
id: resolvedId,
|
|
4324
4378
|
disabled,
|
|
4325
|
-
required
|
|
4379
|
+
required,
|
|
4380
|
+
captionId,
|
|
4381
|
+
errorId,
|
|
4382
|
+
successId
|
|
4326
4383
|
},
|
|
4327
4384
|
children: isHorizontalLayoutNeeded ? /*#__PURE__*/jsxRuntime.jsxs(View, {
|
|
4328
4385
|
ref: ref,
|
|
@@ -4334,10 +4391,7 @@ const FormControl = ({
|
|
|
4334
4391
|
...props,
|
|
4335
4392
|
children: [/*#__PURE__*/jsxRuntime.jsx(View, {
|
|
4336
4393
|
display: 'inline-flex',
|
|
4337
|
-
children: /*#__PURE__*/React.cloneElement(InputComponent,
|
|
4338
|
-
id,
|
|
4339
|
-
disabled
|
|
4340
|
-
})
|
|
4394
|
+
children: /*#__PURE__*/React.cloneElement(InputComponent, inputA11yProps)
|
|
4341
4395
|
}), /*#__PURE__*/jsxRuntime.jsxs(View, {
|
|
4342
4396
|
sx: {
|
|
4343
4397
|
'& > span': {
|
|
@@ -4376,10 +4430,7 @@ const FormControl = ({
|
|
|
4376
4430
|
columnGap: 1
|
|
4377
4431
|
},
|
|
4378
4432
|
children: [relocatableComponentsObject.label, relocatableComponentsObject.tooltipIcon]
|
|
4379
|
-
}), /*#__PURE__*/React.cloneElement(InputComponent,
|
|
4380
|
-
id,
|
|
4381
|
-
disabled
|
|
4382
|
-
}), relocatableComponentsObject.caption, relocatableComponentsObject.errorMessage, relocatableComponentsObject.successMessage]
|
|
4433
|
+
}), /*#__PURE__*/React.cloneElement(InputComponent, inputA11yProps), relocatableComponentsObject.caption, relocatableComponentsObject.errorMessage, relocatableComponentsObject.successMessage]
|
|
4383
4434
|
})
|
|
4384
4435
|
});
|
|
4385
4436
|
};
|
|
@@ -5040,16 +5091,26 @@ const ClickArea = ({
|
|
|
5040
5091
|
disabled,
|
|
5041
5092
|
onClick,
|
|
5042
5093
|
...props
|
|
5043
|
-
}, ref) =>
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5094
|
+
}, ref) => {
|
|
5095
|
+
const handleKeyDown = e => {
|
|
5096
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
5097
|
+
e.preventDefault();
|
|
5098
|
+
e.currentTarget.click();
|
|
5099
|
+
}
|
|
5100
|
+
};
|
|
5101
|
+
return /*#__PURE__*/jsxRuntime.jsx(BaseClickArea, {
|
|
5102
|
+
ref: ref,
|
|
5103
|
+
...props,
|
|
5104
|
+
role: 'button',
|
|
5105
|
+
tabIndex: disabled ? -1 : 0,
|
|
5106
|
+
"aria-disabled": disabled,
|
|
5107
|
+
disabled: disabled,
|
|
5108
|
+
...(!disabled ? {
|
|
5109
|
+
onClick,
|
|
5110
|
+
onKeyDown: handleKeyDown
|
|
5111
|
+
} : {})
|
|
5112
|
+
});
|
|
5113
|
+
};
|
|
5053
5114
|
const BaseClickArea = /*#__PURE__*/styled__default.default(View).withConfig({
|
|
5054
5115
|
displayName: "ClickArea__BaseClickArea",
|
|
5055
5116
|
componentId: "sc-1pd8ned-0"
|
|
@@ -5335,6 +5396,7 @@ const DescriptionList = ({
|
|
|
5335
5396
|
children: [/*#__PURE__*/jsxRuntime.jsx(Grid$1.Unit, {
|
|
5336
5397
|
size: titleUnitSize,
|
|
5337
5398
|
children: /*#__PURE__*/jsxRuntime.jsxs(View, {
|
|
5399
|
+
role: 'term',
|
|
5338
5400
|
display: 'flex',
|
|
5339
5401
|
alignItems: 'center',
|
|
5340
5402
|
flexWrap: 'nowrap',
|
|
@@ -5349,7 +5411,10 @@ const DescriptionList = ({
|
|
|
5349
5411
|
})
|
|
5350
5412
|
}), /*#__PURE__*/jsxRuntime.jsx(Grid$1.Unit, {
|
|
5351
5413
|
size: descriptionUnitSize,
|
|
5352
|
-
children:
|
|
5414
|
+
children: /*#__PURE__*/jsxRuntime.jsx(View, {
|
|
5415
|
+
role: 'definition',
|
|
5416
|
+
children: renderDescription(!utils.isNullable(description) ? description : '-')
|
|
5417
|
+
})
|
|
5353
5418
|
})]
|
|
5354
5419
|
});
|
|
5355
5420
|
},
|
|
@@ -5370,6 +5435,13 @@ const IconButton = /*#__PURE__*/React.forwardRef(({
|
|
|
5370
5435
|
loading = false,
|
|
5371
5436
|
...props
|
|
5372
5437
|
}, ref) => {
|
|
5438
|
+
const hasAccessibleName = Boolean(props['aria-label'] || props['aria-labelledby'] || props['title']);
|
|
5439
|
+
React.useEffect(() => {
|
|
5440
|
+
if (process.env.NODE_ENV !== 'production' && !hasAccessibleName) {
|
|
5441
|
+
// eslint-disable-next-line no-console
|
|
5442
|
+
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.');
|
|
5443
|
+
}
|
|
5444
|
+
}, [hasAccessibleName]);
|
|
5373
5445
|
return /*#__PURE__*/jsxRuntime.jsx(BaseIconButton, {
|
|
5374
5446
|
ref: ref,
|
|
5375
5447
|
icon: Icon,
|
|
@@ -5864,6 +5936,7 @@ const Dialog = ({
|
|
|
5864
5936
|
icon: icons.CloseIcon,
|
|
5865
5937
|
variant: 'plain-bold',
|
|
5866
5938
|
size: 'm',
|
|
5939
|
+
"aria-label": theme.locales?.Dialog?.closeButtonLabel ?? '닫기',
|
|
5867
5940
|
onClick: handleDismiss
|
|
5868
5941
|
})
|
|
5869
5942
|
}), /*#__PURE__*/jsxRuntime.jsx(DialogContext.Provider, {
|
|
@@ -6009,6 +6082,8 @@ const Drawer = ({
|
|
|
6009
6082
|
size = 'm',
|
|
6010
6083
|
direction = 'right',
|
|
6011
6084
|
initialFocusRef,
|
|
6085
|
+
'aria-label': ariaLabel,
|
|
6086
|
+
'aria-labelledby': ariaLabelledby,
|
|
6012
6087
|
sx
|
|
6013
6088
|
}, ref) => {
|
|
6014
6089
|
const theme = styled.useTheme();
|
|
@@ -6033,7 +6108,8 @@ const Drawer = ({
|
|
|
6033
6108
|
useFocusTrap({
|
|
6034
6109
|
containerRef: drawerRef,
|
|
6035
6110
|
initialFocusRef: initialFocusRef || closeButtonRef,
|
|
6036
|
-
disabled: !isOpen
|
|
6111
|
+
disabled: !isOpen,
|
|
6112
|
+
restoreFocusOnCleanUp: true
|
|
6037
6113
|
});
|
|
6038
6114
|
React.useEffect(() => {
|
|
6039
6115
|
if (isOpen && isOutsideClickDismissable) {
|
|
@@ -6116,6 +6192,8 @@ const Drawer = ({
|
|
|
6116
6192
|
className: `trk-drawer--${size} trk-drawer--${direction}`,
|
|
6117
6193
|
ref: drawerRef,
|
|
6118
6194
|
"aria-modal": 'true',
|
|
6195
|
+
"aria-label": ariaLabel,
|
|
6196
|
+
"aria-labelledby": ariaLabelledby,
|
|
6119
6197
|
role: 'dialog',
|
|
6120
6198
|
tabIndex: -1,
|
|
6121
6199
|
size: size,
|
|
@@ -6134,6 +6212,7 @@ const Drawer = ({
|
|
|
6134
6212
|
icon: icons.CloseIcon,
|
|
6135
6213
|
variant: 'plain-bold',
|
|
6136
6214
|
size: 'm',
|
|
6215
|
+
"aria-label": theme.locales?.Drawer?.closeButtonLabel ?? '닫기',
|
|
6137
6216
|
onClick: handleDismiss
|
|
6138
6217
|
})
|
|
6139
6218
|
}), children]
|
|
@@ -6579,9 +6658,12 @@ const Flash = ({
|
|
|
6579
6658
|
return /*#__PURE__*/jsxRuntime.jsxs(BaseFlash, {
|
|
6580
6659
|
ref: ref,
|
|
6581
6660
|
variant: variant,
|
|
6661
|
+
role: variant === 'danger' ? 'alert' : 'status',
|
|
6662
|
+
"aria-live": variant === 'danger' ? 'assertive' : 'polite',
|
|
6582
6663
|
...props,
|
|
6583
6664
|
children: [/*#__PURE__*/jsxRuntime.jsx(Icon, {
|
|
6584
|
-
className: 'flash__leading_icon'
|
|
6665
|
+
className: 'flash__leading_icon',
|
|
6666
|
+
"aria-hidden": true
|
|
6585
6667
|
}), /*#__PURE__*/jsxRuntime.jsxs("div", {
|
|
6586
6668
|
className: 'flash__content',
|
|
6587
6669
|
children: [/*#__PURE__*/jsxRuntime.jsx("span", {
|
|
@@ -6686,20 +6768,30 @@ const IconToggleButton = ({
|
|
|
6686
6768
|
disabled = false,
|
|
6687
6769
|
sx,
|
|
6688
6770
|
...props
|
|
6689
|
-
}, ref) =>
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6771
|
+
}, ref) => {
|
|
6772
|
+
const hasAccessibleName = Boolean(props['aria-label'] || props['aria-labelledby'] || props['title']);
|
|
6773
|
+
React.useEffect(() => {
|
|
6774
|
+
if (process.env.NODE_ENV !== 'production' && !hasAccessibleName) {
|
|
6775
|
+
// eslint-disable-next-line no-console
|
|
6776
|
+
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.');
|
|
6777
|
+
}
|
|
6778
|
+
}, [hasAccessibleName]);
|
|
6779
|
+
return /*#__PURE__*/jsxRuntime.jsx(BaseIconToggleButton, {
|
|
6780
|
+
ref: ref,
|
|
6781
|
+
icon: Icon,
|
|
6782
|
+
size: size,
|
|
6783
|
+
shape: shape,
|
|
6784
|
+
variant: variant,
|
|
6785
|
+
selected: selected,
|
|
6786
|
+
"aria-pressed": selected,
|
|
6787
|
+
type: 'button',
|
|
6788
|
+
disabled: disabled,
|
|
6789
|
+
$disabled: disabled,
|
|
6790
|
+
sx: sx,
|
|
6791
|
+
...props,
|
|
6792
|
+
children: /*#__PURE__*/jsxRuntime.jsx(Icon, {})
|
|
6793
|
+
});
|
|
6794
|
+
};
|
|
6703
6795
|
const BaseIconToggleButton = /*#__PURE__*/styled__default.default(UnstyledButton).withConfig({
|
|
6704
6796
|
displayName: "IconToggleButton__BaseIconToggleButton",
|
|
6705
6797
|
componentId: "sc-1y68w0-0"
|
|
@@ -7307,6 +7399,7 @@ const TabItem = ({
|
|
|
7307
7399
|
className: 'trk-tab_item',
|
|
7308
7400
|
type: 'button',
|
|
7309
7401
|
role: 'tab',
|
|
7402
|
+
"aria-selected": selected,
|
|
7310
7403
|
ref: ref,
|
|
7311
7404
|
variant: variant,
|
|
7312
7405
|
size: size,
|
|
@@ -7587,6 +7680,7 @@ const Tab = ({
|
|
|
7587
7680
|
size: 's',
|
|
7588
7681
|
variant: 'plain-bold',
|
|
7589
7682
|
icon: icons.ChevronLeftIcon,
|
|
7683
|
+
"aria-label": theme.locales?.Tab?.scrollLeftLabel ?? '왼쪽으로 스크롤',
|
|
7590
7684
|
onClick: handleLeftButtonClick
|
|
7591
7685
|
})
|
|
7592
7686
|
})]
|
|
@@ -7618,6 +7712,7 @@ const Tab = ({
|
|
|
7618
7712
|
size: 's',
|
|
7619
7713
|
variant: 'plain-bold',
|
|
7620
7714
|
icon: icons.ChevronRightIcon,
|
|
7715
|
+
"aria-label": theme.locales?.Tab?.scrollRightLabel ?? '오른쪽으로 스크롤',
|
|
7621
7716
|
onClick: handleRightButtonClick
|
|
7622
7717
|
})
|
|
7623
7718
|
})]
|
|
@@ -7660,6 +7755,16 @@ const theme = {
|
|
|
7660
7755
|
UploadInput: {
|
|
7661
7756
|
placeholder: '파일을 끌어다 놓으세요',
|
|
7662
7757
|
selectFile: '파일 선택'
|
|
7758
|
+
},
|
|
7759
|
+
Dialog: {
|
|
7760
|
+
closeButtonLabel: '닫기'
|
|
7761
|
+
},
|
|
7762
|
+
Drawer: {
|
|
7763
|
+
closeButtonLabel: '닫기'
|
|
7764
|
+
},
|
|
7765
|
+
Tab: {
|
|
7766
|
+
scrollLeftLabel: '왼쪽으로 스크롤',
|
|
7767
|
+
scrollRightLabel: '오른쪽으로 스크롤'
|
|
7663
7768
|
}
|
|
7664
7769
|
}
|
|
7665
7770
|
};
|
|
@@ -7697,13 +7802,18 @@ const Toast = ({
|
|
|
7697
7802
|
variant = 'success',
|
|
7698
7803
|
icon: Icon = variant === 'success' ? icons.CheckInCircleIcon : icons.ExclamationPointInCircleIcon,
|
|
7699
7804
|
resizing = 'hug',
|
|
7700
|
-
children
|
|
7805
|
+
children,
|
|
7806
|
+
...props
|
|
7701
7807
|
}) => {
|
|
7702
7808
|
return /*#__PURE__*/jsxRuntime.jsxs(BaseToast, {
|
|
7703
7809
|
variant: variant,
|
|
7704
7810
|
resizing: resizing,
|
|
7811
|
+
role: variant === 'warning' ? 'alert' : 'status',
|
|
7812
|
+
"aria-live": variant === 'warning' ? 'assertive' : 'polite',
|
|
7813
|
+
...props,
|
|
7705
7814
|
children: [/*#__PURE__*/jsxRuntime.jsx(Icon, {
|
|
7706
|
-
className: 'toast__leading_icon'
|
|
7815
|
+
className: 'toast__leading_icon',
|
|
7816
|
+
"aria-hidden": true
|
|
7707
7817
|
}), children]
|
|
7708
7818
|
});
|
|
7709
7819
|
};
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -330,6 +330,16 @@ declare const theme: {
|
|
|
330
330
|
placeholder: string;
|
|
331
331
|
selectFile: string;
|
|
332
332
|
};
|
|
333
|
+
Dialog: {
|
|
334
|
+
closeButtonLabel: string;
|
|
335
|
+
};
|
|
336
|
+
Drawer: {
|
|
337
|
+
closeButtonLabel: string;
|
|
338
|
+
};
|
|
339
|
+
Tab: {
|
|
340
|
+
scrollLeftLabel: string;
|
|
341
|
+
scrollRightLabel: string;
|
|
342
|
+
};
|
|
333
343
|
};
|
|
334
344
|
};
|
|
335
345
|
declare const gpaiLightTheme: {
|
|
@@ -664,6 +674,16 @@ declare const gpaiLightTheme: {
|
|
|
664
674
|
placeholder: string;
|
|
665
675
|
selectFile: string;
|
|
666
676
|
};
|
|
677
|
+
Dialog: {
|
|
678
|
+
closeButtonLabel: string;
|
|
679
|
+
};
|
|
680
|
+
Drawer: {
|
|
681
|
+
closeButtonLabel: string;
|
|
682
|
+
};
|
|
683
|
+
Tab: {
|
|
684
|
+
scrollLeftLabel: string;
|
|
685
|
+
scrollRightLabel: string;
|
|
686
|
+
};
|
|
667
687
|
};
|
|
668
688
|
};
|
|
669
689
|
declare const gpaiDarkTheme: {
|
|
@@ -998,6 +1018,16 @@ declare const gpaiDarkTheme: {
|
|
|
998
1018
|
placeholder: string;
|
|
999
1019
|
selectFile: string;
|
|
1000
1020
|
};
|
|
1021
|
+
Dialog: {
|
|
1022
|
+
closeButtonLabel: string;
|
|
1023
|
+
};
|
|
1024
|
+
Drawer: {
|
|
1025
|
+
closeButtonLabel: string;
|
|
1026
|
+
};
|
|
1027
|
+
Tab: {
|
|
1028
|
+
scrollLeftLabel: string;
|
|
1029
|
+
scrollRightLabel: string;
|
|
1030
|
+
};
|
|
1001
1031
|
};
|
|
1002
1032
|
};
|
|
1003
1033
|
export default theme;
|
|
@@ -7,16 +7,26 @@ const ClickArea = ({
|
|
|
7
7
|
disabled,
|
|
8
8
|
onClick,
|
|
9
9
|
...props
|
|
10
|
-
}, ref) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
}, ref) => {
|
|
11
|
+
const handleKeyDown = e => {
|
|
12
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
e.currentTarget.click();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return /*#__PURE__*/jsx(BaseClickArea, {
|
|
18
|
+
ref: ref,
|
|
19
|
+
...props,
|
|
20
|
+
role: 'button',
|
|
21
|
+
tabIndex: disabled ? -1 : 0,
|
|
22
|
+
"aria-disabled": disabled,
|
|
23
|
+
disabled: disabled,
|
|
24
|
+
...(!disabled ? {
|
|
25
|
+
onClick,
|
|
26
|
+
onKeyDown: handleKeyDown
|
|
27
|
+
} : {})
|
|
28
|
+
});
|
|
29
|
+
};
|
|
20
30
|
const BaseClickArea = /*#__PURE__*/styled(View).withConfig({
|
|
21
31
|
displayName: "ClickArea__BaseClickArea",
|
|
22
32
|
componentId: "sc-1pd8ned-0"
|
|
@@ -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
|
}
|