@ncds/ui-admin 1.8.18 → 1.8.19-alpha.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.
@@ -16,7 +16,7 @@ const BADGE_ICON_SIZE = 12;
16
16
  /** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
17
17
  const NEW_BADGE_ICON_SIZE = {
18
18
  sm: 12,
19
- md: 16
19
+ md: 14
20
20
  };
21
21
  const Badge = _ref => {
22
22
  let {
@@ -35,6 +35,8 @@ const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref,
35
35
  showHelpIcon,
36
36
  maxLength,
37
37
  showTextCount,
38
+ clearText,
39
+ onClearText,
38
40
  className,
39
41
  ...props
40
42
  } = _ref;
@@ -100,11 +102,11 @@ const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref,
100
102
  }
101
103
  };
102
104
  const renderClearButton = () => {
103
- if (!props.clearText) return null;
105
+ if (!clearText) return null;
104
106
  return (0, _jsxRuntime.jsx)("button", {
105
107
  type: "button",
106
108
  className: (0, _classnames.default)('ncua-input__icon-wrap', 'ncua-input__right-icon', 'ncua-input__clear'),
107
- onClick: props.onClearText,
109
+ onClick: onClearText,
108
110
  children: (0, _jsxRuntime.jsx)(_uiAdminIcon.X, {
109
111
  className: "ncua-input__clear-icon",
110
112
  width: validationSvgSize[size],
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ var _react = require("react");
4
+ var _client = require("react-dom/client");
5
+ var _testUtils = require("react-dom/test-utils");
6
+ var _vitest = require("vitest");
7
+ var _InputBase = require("../InputBase");
8
+ // @vitest-environment jsdom
9
+
10
+ // React 18에서 act() 사용 시 필요한 플래그 (미설정 시 console.error 경고 발생)
11
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
12
+ function mountInputBase() {
13
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
14
+ const container = document.createElement('div');
15
+ document.body.appendChild(container);
16
+ const root = (0, _client.createRoot)(container);
17
+ (0, _testUtils.act)(() => {
18
+ root.render(/*#__PURE__*/(0, _react.createElement)(_InputBase.InputBase, props));
19
+ });
20
+ const unmount = () => {
21
+ (0, _testUtils.act)(() => {
22
+ root.unmount();
23
+ });
24
+ container.remove();
25
+ };
26
+ return {
27
+ container,
28
+ unmount
29
+ };
30
+ }
31
+ (0, _vitest.describe)('InputBase — clearText prop DOM 누출', () => {
32
+ (0, _vitest.it)('clearText/onClearText가 DOM input 속성으로 전달되지 않는다', () => {
33
+ // Given: console.error를 감시하는 상태에서
34
+ const consoleErrorSpy = _vitest.vi.spyOn(console, 'error');
35
+ // When: clearText, onClearText를 전달해 렌더하면
36
+ const view = mountInputBase({
37
+ clearText: true,
38
+ onClearText: _vitest.vi.fn()
39
+ });
40
+ // Then: 렌더된 <input>에 cleartext 속성이 없고, unknown prop 경고도 발생하지 않는다
41
+ const input = view.container.querySelector('input');
42
+ (0, _vitest.expect)(input).not.toBeNull();
43
+ (0, _vitest.expect)(input?.hasAttribute('cleartext')).toBe(false);
44
+ (0, _vitest.expect)(consoleErrorSpy).not.toHaveBeenCalled();
45
+ consoleErrorSpy.mockRestore();
46
+ view.unmount();
47
+ });
48
+ (0, _vitest.it)('clearText가 true면 clear 버튼이 렌더되고 클릭 시 onClearText가 호출된다', () => {
49
+ // Given: clearText와 onClearText를 전달해 렌더한 상태에서
50
+ const onClearText = _vitest.vi.fn();
51
+ const view = mountInputBase({
52
+ clearText: true,
53
+ onClearText
54
+ });
55
+ // When: clear 버튼을 찾아 클릭하면
56
+ const clearButton = view.container.querySelector('.ncua-input__clear');
57
+ (0, _vitest.expect)(clearButton).not.toBeNull();
58
+ (0, _testUtils.act)(() => {
59
+ clearButton?.click();
60
+ });
61
+ // Then: onClearText가 호출된다
62
+ (0, _vitest.expect)(onClearText).toHaveBeenCalledTimes(1);
63
+ view.unmount();
64
+ });
65
+ (0, _vitest.it)('clearText를 전달하지 않으면 clear 버튼이 렌더되지 않는다', () => {
66
+ // Given/When: clearText 없이 렌더하면
67
+ const view = mountInputBase();
68
+ // Then: clear 버튼이 존재하지 않는다
69
+ (0, _vitest.expect)(view.container.querySelector('.ncua-input__clear')).toBeNull();
70
+ view.unmount();
71
+ });
72
+ });
@@ -9,7 +9,7 @@ const BADGE_ICON_SIZE = 12;
9
9
  /** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
10
10
  const NEW_BADGE_ICON_SIZE = {
11
11
  sm: 12,
12
- md: 16
12
+ md: 14
13
13
  };
14
14
  const Badge = _ref => {
15
15
  let {
@@ -28,6 +28,8 @@ const InputBase = /*#__PURE__*/forwardRef((_ref, ref) => {
28
28
  showHelpIcon,
29
29
  maxLength,
30
30
  showTextCount,
31
+ clearText,
32
+ onClearText,
31
33
  className,
32
34
  ...props
33
35
  } = _ref;
@@ -93,11 +95,11 @@ const InputBase = /*#__PURE__*/forwardRef((_ref, ref) => {
93
95
  }
94
96
  };
95
97
  const renderClearButton = () => {
96
- if (!props.clearText) return null;
98
+ if (!clearText) return null;
97
99
  return _jsx("button", {
98
100
  type: "button",
99
101
  className: classNames('ncua-input__icon-wrap', 'ncua-input__right-icon', 'ncua-input__clear'),
100
- onClick: props.onClearText,
102
+ onClick: onClearText,
101
103
  children: _jsx(X, {
102
104
  className: "ncua-input__clear-icon",
103
105
  width: validationSvgSize[size],
@@ -0,0 +1,69 @@
1
+ // @vitest-environment jsdom
2
+ import { createElement } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { act } from 'react-dom/test-utils';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+ import { InputBase } from '../InputBase';
7
+ // React 18에서 act() 사용 시 필요한 플래그 (미설정 시 console.error 경고 발생)
8
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
9
+ function mountInputBase() {
10
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11
+ const container = document.createElement('div');
12
+ document.body.appendChild(container);
13
+ const root = createRoot(container);
14
+ act(() => {
15
+ root.render(/*#__PURE__*/createElement(InputBase, props));
16
+ });
17
+ const unmount = () => {
18
+ act(() => {
19
+ root.unmount();
20
+ });
21
+ container.remove();
22
+ };
23
+ return {
24
+ container,
25
+ unmount
26
+ };
27
+ }
28
+ describe('InputBase — clearText prop DOM 누출', () => {
29
+ it('clearText/onClearText가 DOM input 속성으로 전달되지 않는다', () => {
30
+ // Given: console.error를 감시하는 상태에서
31
+ const consoleErrorSpy = vi.spyOn(console, 'error');
32
+ // When: clearText, onClearText를 전달해 렌더하면
33
+ const view = mountInputBase({
34
+ clearText: true,
35
+ onClearText: vi.fn()
36
+ });
37
+ // Then: 렌더된 <input>에 cleartext 속성이 없고, unknown prop 경고도 발생하지 않는다
38
+ const input = view.container.querySelector('input');
39
+ expect(input).not.toBeNull();
40
+ expect(input?.hasAttribute('cleartext')).toBe(false);
41
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
42
+ consoleErrorSpy.mockRestore();
43
+ view.unmount();
44
+ });
45
+ it('clearText가 true면 clear 버튼이 렌더되고 클릭 시 onClearText가 호출된다', () => {
46
+ // Given: clearText와 onClearText를 전달해 렌더한 상태에서
47
+ const onClearText = vi.fn();
48
+ const view = mountInputBase({
49
+ clearText: true,
50
+ onClearText
51
+ });
52
+ // When: clear 버튼을 찾아 클릭하면
53
+ const clearButton = view.container.querySelector('.ncua-input__clear');
54
+ expect(clearButton).not.toBeNull();
55
+ act(() => {
56
+ clearButton?.click();
57
+ });
58
+ // Then: onClearText가 호출된다
59
+ expect(onClearText).toHaveBeenCalledTimes(1);
60
+ view.unmount();
61
+ });
62
+ it('clearText를 전달하지 않으면 clear 버튼이 렌더되지 않는다', () => {
63
+ // Given/When: clearText 없이 렌더하면
64
+ const view = mountInputBase();
65
+ // Then: clear 버튼이 존재하지 않는다
66
+ expect(view.container.querySelector('.ncua-input__clear')).toBeNull();
67
+ view.unmount();
68
+ });
69
+ });
@@ -16,4 +16,4 @@ type BadgeProps = {
16
16
  };
17
17
  declare const Badge: ({ label, type, color, className, leadingIcon, trailingIcon, size, }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
18
18
  export { Badge };
19
- export type { BadgeType, BadgeColor, BadgeSize, BadgeProps };
19
+ export type { BadgeColor, BadgeProps, BadgeSize, BadgeType };
@@ -7,7 +7,7 @@ import { sideSlotRender } from './utils';
7
7
  */
8
8
  const BADGE_ICON_SIZE = 12;
9
9
  /** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
10
- const NEW_BADGE_ICON_SIZE = { sm: 12, md: 16 };
10
+ const NEW_BADGE_ICON_SIZE = { sm: 12, md: 14 };
11
11
  const Badge = ({ label, type = 'pill-outline', color = 'neutral', className, leadingIcon, trailingIcon, size = 'xs', }) => {
12
12
  // new-badge: 신규 콘텐츠 'N' 마크 전용 타입. label·color·leadingIcon·trailingIcon은 무시되고
13
13
  // 색은 --pink-600 으로 고정된다. (디자이너 명세: Icon/FeaturedIcon/Badge로 대체 불가한 전용 표시)
@@ -13,7 +13,7 @@ const generalSvgSize = {
13
13
  xs: 14,
14
14
  sm: 20,
15
15
  };
16
- const InputBase = forwardRef(({ size = 'xs', required, label, hintText, disabled, fullWidth = false, validation, destructive, leadingElement, trailingElement, showHelpIcon, maxLength, showTextCount, className, ...props }, ref) => {
16
+ const InputBase = forwardRef(({ size = 'xs', required, label, hintText, disabled, fullWidth = false, validation, destructive, leadingElement, trailingElement, showHelpIcon, maxLength, showTextCount, clearText, onClearText, className, ...props }, ref) => {
17
17
  const inputRef = useRef(null);
18
18
  const [textCount, setTextCount] = useState(0);
19
19
  useEffect(() => {
@@ -59,9 +59,9 @@ const InputBase = forwardRef(({ size = 'xs', required, label, hintText, disabled
59
59
  }
60
60
  };
61
61
  const renderClearButton = () => {
62
- if (!props.clearText)
62
+ if (!clearText)
63
63
  return null;
64
- return (_jsx("button", { type: "button", className: classNames('ncua-input__icon-wrap', 'ncua-input__right-icon', 'ncua-input__clear'), onClick: props.onClearText, children: _jsx(X, { className: "ncua-input__clear-icon", width: validationSvgSize[size], height: validationSvgSize[size] }) }));
64
+ return (_jsx("button", { type: "button", className: classNames('ncua-input__icon-wrap', 'ncua-input__right-icon', 'ncua-input__clear'), onClick: onClearText, children: _jsx(X, { className: "ncua-input__clear-icon", width: validationSvgSize[size], height: validationSvgSize[size] }) }));
65
65
  };
66
66
  const renderStatusIcon = () => {
67
67
  if (destructive) {
@@ -0,0 +1,59 @@
1
+ // @vitest-environment jsdom
2
+ import { createElement } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { act } from 'react-dom/test-utils';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+ import { InputBase } from '../InputBase';
7
+ // React 18에서 act() 사용 시 필요한 플래그 (미설정 시 console.error 경고 발생)
8
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
9
+ function mountInputBase(props = {}) {
10
+ const container = document.createElement('div');
11
+ document.body.appendChild(container);
12
+ const root = createRoot(container);
13
+ act(() => {
14
+ root.render(createElement(InputBase, props));
15
+ });
16
+ const unmount = () => {
17
+ act(() => {
18
+ root.unmount();
19
+ });
20
+ container.remove();
21
+ };
22
+ return { container, unmount };
23
+ }
24
+ describe('InputBase — clearText prop DOM 누출', () => {
25
+ it('clearText/onClearText가 DOM input 속성으로 전달되지 않는다', () => {
26
+ // Given: console.error를 감시하는 상태에서
27
+ const consoleErrorSpy = vi.spyOn(console, 'error');
28
+ // When: clearText, onClearText를 전달해 렌더하면
29
+ const view = mountInputBase({ clearText: true, onClearText: vi.fn() });
30
+ // Then: 렌더된 <input>에 cleartext 속성이 없고, unknown prop 경고도 발생하지 않는다
31
+ const input = view.container.querySelector('input');
32
+ expect(input).not.toBeNull();
33
+ expect(input?.hasAttribute('cleartext')).toBe(false);
34
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
35
+ consoleErrorSpy.mockRestore();
36
+ view.unmount();
37
+ });
38
+ it('clearText가 true면 clear 버튼이 렌더되고 클릭 시 onClearText가 호출된다', () => {
39
+ // Given: clearText와 onClearText를 전달해 렌더한 상태에서
40
+ const onClearText = vi.fn();
41
+ const view = mountInputBase({ clearText: true, onClearText });
42
+ // When: clear 버튼을 찾아 클릭하면
43
+ const clearButton = view.container.querySelector('.ncua-input__clear');
44
+ expect(clearButton).not.toBeNull();
45
+ act(() => {
46
+ clearButton?.click();
47
+ });
48
+ // Then: onClearText가 호출된다
49
+ expect(onClearText).toHaveBeenCalledTimes(1);
50
+ view.unmount();
51
+ });
52
+ it('clearText를 전달하지 않으면 clear 버튼이 렌더되지 않는다', () => {
53
+ // Given/When: clearText 없이 렌더하면
54
+ const view = mountInputBase();
55
+ // Then: clear 버튼이 존재하지 않는다
56
+ expect(view.container.querySelector('.ncua-input__clear')).toBeNull();
57
+ view.unmount();
58
+ });
59
+ });
@@ -16,4 +16,4 @@ type BadgeProps = {
16
16
  };
17
17
  declare const Badge: ({ label, type, color, className, leadingIcon, trailingIcon, size, }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
18
18
  export { Badge };
19
- export type { BadgeType, BadgeColor, BadgeSize, BadgeProps };
19
+ export type { BadgeColor, BadgeProps, BadgeSize, BadgeType };
@@ -5039,15 +5039,16 @@ button {
5039
5039
  justify-content: center;
5040
5040
  color: var(--pink-600);
5041
5041
  background-color: var(--pink-200);
5042
- border-radius: 5px;
5043
5042
  }
5044
5043
  .ncua-badge--new-badge-sm {
5045
5044
  width: 16px;
5046
5045
  height: 16px;
5046
+ border-radius: 3px;
5047
5047
  }
5048
5048
  .ncua-badge--new-badge-md {
5049
5049
  width: 20px;
5050
5050
  height: 20px;
5051
+ border-radius: 4px;
5051
5052
  }
5052
5053
 
5053
5054
  .ncua-badge-group {
@@ -6309,7 +6310,7 @@ button {
6309
6310
  bottom: 0;
6310
6311
  width: 3px;
6311
6312
  background: var(--base-black);
6312
- z-index: 1;
6313
+ z-index: var(--z-index-1);
6313
6314
  }
6314
6315
  .ncua-table__row--warning.ncua-table__row--selected:nth-child(n) {
6315
6316
  background: var(--orange-50);
@@ -6454,7 +6455,7 @@ button {
6454
6455
  .ncua-table--fixed-header .ncua-table__header-cell {
6455
6456
  position: sticky;
6456
6457
  top: 0;
6457
- z-index: 10;
6458
+ z-index: var(--z-index-sticky);
6458
6459
  padding: 0 var(--spacing-m);
6459
6460
  height: var(--ncua-table-header-height);
6460
6461
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncds/ui-admin",
3
- "version": "1.8.18",
3
+ "version": "1.8.19-alpha.0",
4
4
  "description": "nhn-commerce의 어드민 디자인 시스템입니다.",
5
5
  "scripts": {
6
6
  "barrel": "node barrel.js",