@ncds/ui-admin 1.8.21-alpha.8 → 1.8.21
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/cjs/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/cjs/src/components/forms-and-input/input-base/InputBase.js +13 -7
- package/dist/cjs/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +16 -11
- package/dist/cjs/src/components/navigation/pagination/Pagination.js +3 -1
- package/dist/cjs/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
- package/dist/cjs/src/components/overlays/notification/CalloutNotification.js +6 -1
- package/dist/cjs/src/components/overlays/notification/Notification.js +22 -2
- package/dist/cjs/src/components/overlays/notification/__tests__/CalloutNotification.test.js +128 -0
- package/dist/cjs/src/components/overlays/notification/__tests__/Notification.test.js +83 -0
- package/dist/cjs/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +72 -90
- package/dist/cjs/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +70 -0
- package/dist/cjs/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +96 -39
- package/dist/cjs/src/generated/scoped-css.js +1 -1
- package/dist/esm/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/esm/src/components/forms-and-input/input-base/InputBase.js +13 -7
- package/dist/esm/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +16 -11
- package/dist/esm/src/components/navigation/pagination/Pagination.js +3 -1
- package/dist/esm/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
- package/dist/esm/src/components/overlays/notification/CalloutNotification.js +6 -1
- package/dist/esm/src/components/overlays/notification/Notification.js +22 -2
- package/dist/esm/src/components/overlays/notification/__tests__/CalloutNotification.test.js +125 -0
- package/dist/esm/src/components/overlays/notification/__tests__/Notification.test.js +80 -0
- package/dist/esm/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +72 -90
- package/dist/esm/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +63 -0
- package/dist/esm/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +96 -39
- package/dist/esm/src/generated/scoped-css.js +1 -1
- package/dist/temp/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
- package/dist/temp/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/temp/src/components/forms-and-input/input-base/InputBase.js +10 -4
- package/dist/temp/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +14 -12
- package/dist/temp/src/components/navigation/pagination/Pagination.js +3 -1
- package/dist/temp/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
- package/dist/temp/src/components/overlays/notification/CalloutNotification.d.ts +3 -1
- package/dist/temp/src/components/overlays/notification/CalloutNotification.js +5 -1
- package/dist/temp/src/components/overlays/notification/Notification.d.ts +12 -2
- package/dist/temp/src/components/overlays/notification/Notification.js +9 -4
- package/dist/temp/src/components/overlays/notification/__tests__/CalloutNotification.test.d.ts +1 -0
- package/dist/temp/src/components/overlays/notification/__tests__/CalloutNotification.test.js +108 -0
- package/dist/temp/src/components/overlays/notification/__tests__/Notification.test.d.ts +1 -0
- package/dist/temp/src/components/overlays/notification/__tests__/Notification.test.js +71 -0
- package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModal.d.ts +2 -6
- package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +38 -27
- package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.d.ts +16 -0
- package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +24 -0
- package/dist/temp/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +82 -35
- package/dist/temp/src/generated/scoped-css.js +1 -1
- package/dist/types/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
- package/dist/types/src/components/overlays/notification/CalloutNotification.d.ts +3 -1
- package/dist/types/src/components/overlays/notification/Notification.d.ts +12 -2
- package/dist/types/src/components/overlays/notification/__tests__/CalloutNotification.test.d.ts +1 -0
- package/dist/types/src/components/overlays/notification/__tests__/Notification.test.d.ts +1 -0
- package/dist/types/src/components/overlays/postcode-search-modal/PostcodeSearchModal.d.ts +2 -6
- package/dist/types/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.d.ts +16 -0
- package/dist/ui-admin/assets/styles/style.css +91 -33
- package/package.json +1 -1
- package/dist/ui-admin/assets/styles/style.scoped.css +0 -8557
|
@@ -12,12 +12,18 @@ var _color = require("../../../../constant/color");
|
|
|
12
12
|
var _HintText = require("../../shared/hintText/HintText");
|
|
13
13
|
var _Label = require("../../shared/label/Label");
|
|
14
14
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
// 슬롯 아이콘(leadingElement/trailingElement)
|
|
15
|
+
// 슬롯 아이콘(leadingElement/trailingElement)은 인풋 크기에 맞춰 커진다.
|
|
16
16
|
// slot.size 를 명시하면 그 값이 우선한다.
|
|
17
17
|
const iconSvgSize = {
|
|
18
18
|
xs: 14,
|
|
19
19
|
sm: 20
|
|
20
20
|
};
|
|
21
|
+
// 상태 아이콘(clear·validation·destructive)은 인풋 크기와 무관하게 16px 로 고정한다.
|
|
22
|
+
// 슬롯 아이콘은 sm 에서 20px 로 커지므로 두 상수를 하나로 합치지 않는다.
|
|
23
|
+
const statusIconSvgSize = {
|
|
24
|
+
xs: 14,
|
|
25
|
+
sm: 16
|
|
26
|
+
};
|
|
21
27
|
const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
22
28
|
let {
|
|
23
29
|
size = 'xs',
|
|
@@ -125,8 +131,8 @@ const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref,
|
|
|
125
131
|
onClick: onClearText,
|
|
126
132
|
children: (0, _jsxRuntime.jsx)(_uiAdminIcon.X, {
|
|
127
133
|
className: "ncua-input__clear-icon",
|
|
128
|
-
width:
|
|
129
|
-
height:
|
|
134
|
+
width: statusIconSvgSize[size],
|
|
135
|
+
height: statusIconSvgSize[size]
|
|
130
136
|
})
|
|
131
137
|
});
|
|
132
138
|
};
|
|
@@ -136,8 +142,8 @@ const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref,
|
|
|
136
142
|
className: "ncua-input__icon-wrap ncua-input__right-icon",
|
|
137
143
|
children: (0, _jsxRuntime.jsx)(_uiAdminIcon.InfoCircle, {
|
|
138
144
|
className: "ncua-input__destructive-icon",
|
|
139
|
-
width:
|
|
140
|
-
height:
|
|
145
|
+
width: statusIconSvgSize[size],
|
|
146
|
+
height: statusIconSvgSize[size]
|
|
141
147
|
})
|
|
142
148
|
});
|
|
143
149
|
}
|
|
@@ -146,8 +152,8 @@ const InputBase = exports.InputBase = /*#__PURE__*/(0, _react.forwardRef)((_ref,
|
|
|
146
152
|
className: "ncua-input__icon-wrap ncua-input__right-icon",
|
|
147
153
|
children: (0, _jsxRuntime.jsx)(_uiAdminIcon.CheckCircle, {
|
|
148
154
|
className: "ncua-input__validation-icon",
|
|
149
|
-
width:
|
|
150
|
-
height:
|
|
155
|
+
width: statusIconSvgSize[size],
|
|
156
|
+
height: statusIconSvgSize[size]
|
|
151
157
|
})
|
|
152
158
|
});
|
|
153
159
|
}
|
|
@@ -80,8 +80,9 @@ function mountInputBase() {
|
|
|
80
80
|
height: svg?.getAttribute('height')
|
|
81
81
|
};
|
|
82
82
|
};
|
|
83
|
-
// 슬롯
|
|
84
|
-
|
|
83
|
+
// 슬롯 아이콘은 인풋 크기에 연동돼 sm 에서 20px 로 커지지만,
|
|
84
|
+
// 상태 아이콘(clear·validation·destructive)은 16px 로 고정한다.
|
|
85
|
+
_vitest.it.each([['xs', '14', '14'], ['sm', '20', '16']])('size=%s 일 때 슬롯 아이콘은 %spx, 상태 아이콘은 %spx로 렌더된다', (size, slotExpected, statusExpected) => {
|
|
85
86
|
// Given/When: 슬롯 아이콘과 clear 버튼, destructive 상태를 함께 렌더하면
|
|
86
87
|
const view = mountInputBase({
|
|
87
88
|
size,
|
|
@@ -93,23 +94,27 @@ function mountInputBase() {
|
|
|
93
94
|
icon: _uiAdminIcon.SearchLg
|
|
94
95
|
}
|
|
95
96
|
});
|
|
96
|
-
// Then:
|
|
97
|
-
const
|
|
98
|
-
width:
|
|
99
|
-
height:
|
|
97
|
+
// Then: 슬롯 아이콘만 인풋 크기를 따라가고, 상태 아이콘은 16px 을 넘지 않는다
|
|
98
|
+
const slotSize = {
|
|
99
|
+
width: slotExpected,
|
|
100
|
+
height: slotExpected
|
|
101
|
+
};
|
|
102
|
+
const statusSize = {
|
|
103
|
+
width: statusExpected,
|
|
104
|
+
height: statusExpected
|
|
100
105
|
};
|
|
101
|
-
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__left-icon')).toEqual(
|
|
102
|
-
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__clear-icon')).toEqual(
|
|
103
|
-
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__destructive-icon')).toEqual(
|
|
106
|
+
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__left-icon')).toEqual(slotSize);
|
|
107
|
+
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__clear-icon')).toEqual(statusSize);
|
|
108
|
+
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__destructive-icon')).toEqual(statusSize);
|
|
104
109
|
view.unmount();
|
|
105
110
|
});
|
|
106
|
-
_vitest.it.each([['xs', '14'], ['sm', '
|
|
111
|
+
_vitest.it.each([['xs', '14'], ['sm', '16']])('size=%s 일 때 validation 아이콘은 %spx로 렌더된다', (size, expected) => {
|
|
107
112
|
// Given/When: validation 상태로 렌더하면
|
|
108
113
|
const view = mountInputBase({
|
|
109
114
|
size,
|
|
110
115
|
validation: true
|
|
111
116
|
});
|
|
112
|
-
// Then: 다른 아이콘과 같은 크기다
|
|
117
|
+
// Then: 다른 상태 아이콘과 같은 크기다
|
|
113
118
|
(0, _vitest.expect)(iconSizeOf(view.container, '.ncua-input__validation-icon')).toEqual({
|
|
114
119
|
width: expected,
|
|
115
120
|
height: expected
|
|
@@ -26,7 +26,9 @@ const Pagination = _ref => {
|
|
|
26
26
|
const [start, setStart] = (0, _react.useState)(1);
|
|
27
27
|
const noPrev = breakPoint === 'mo' ? currentPage === 1 : start === 1;
|
|
28
28
|
const noNext = breakPoint === 'mo' ? currentPage >= totalPage : start + pageCount - 1 >= totalPage;
|
|
29
|
-
|
|
29
|
+
// mo 는 CSS 가 페이지 번호 목록과 first/last 를 숨겨 prev/next 만 남는다.
|
|
30
|
+
// pc 기준(totalPage > pageCount)을 그대로 쓰면 페이지가 적을 때 이동 수단이 하나도 없다.
|
|
31
|
+
const showJumpPageButton = breakPoint === 'mo' ? totalPage > 1 : totalPage > pageCount;
|
|
30
32
|
const handleClickButton = pageNum => {
|
|
31
33
|
if ((0, _lodashEs.isFunction)(onPageChange)) {
|
|
32
34
|
onPageChange(pageNum);
|
|
@@ -15,6 +15,12 @@ const CURRENT_PAGE = 91; // 100페이지 기준 마지막 그룹(91~100)을 보
|
|
|
15
15
|
const GROUP_START_100_PAGES = 91; // currentPage=91 일 때의 그룹 시작
|
|
16
16
|
const GROUP_START_50_PAGES = 41; // totalPage=50 으로 줄었을 때의 마지막 그룹 시작
|
|
17
17
|
const LAST_PAGE_50 = 50;
|
|
18
|
+
/** 페이지 그룹 크기(10) 이하로 나뉘는 건수 — 3페이지 */
|
|
19
|
+
const TOTAL_COUNT_3_PAGES = 30;
|
|
20
|
+
/** TOTAL_COUNT_3_PAGES 기준 총 페이지 수 = 마지막 페이지 번호 */
|
|
21
|
+
const LAST_PAGE_3 = 3;
|
|
22
|
+
/** 페이지가 하나뿐인 건수 */
|
|
23
|
+
const TOTAL_COUNT_1_PAGE = 8;
|
|
18
24
|
let root = null;
|
|
19
25
|
let container = null;
|
|
20
26
|
function render(props) {
|
|
@@ -37,6 +43,8 @@ function render(props) {
|
|
|
37
43
|
container = null;
|
|
38
44
|
});
|
|
39
45
|
const pageNums = el => Array.from(el.querySelectorAll('.ncua-pagination__page-num')).map(b => Number(b.textContent));
|
|
46
|
+
const prevOf = el => el.querySelector('.ncua-pagination__before');
|
|
47
|
+
const nextOf = el => el.querySelector('.ncua-pagination__next');
|
|
40
48
|
(0, _vitest.describe)('#332 Pagination — totalPage 축소 시 start 보정', () => {
|
|
41
49
|
(0, _vitest.it)('totalPage 가 줄면 start 를 마지막 그룹으로 보정하고 범위 밖 페이지 버튼을 표시하지 않는다', () => {
|
|
42
50
|
// 1) 100 페이지에서 91 페이지 보는 중 → 91~100 그룹 표시
|
|
@@ -59,4 +67,70 @@ const pageNums = el => Array.from(el.querySelectorAll('.ncua-pagination__page-nu
|
|
|
59
67
|
(0, _vitest.expect)(nums[0]).toBe(GROUP_START_50_PAGES);
|
|
60
68
|
(0, _vitest.expect)(Math.max(...nums)).toBeLessThanOrEqual(LAST_PAGE_50);
|
|
61
69
|
});
|
|
70
|
+
});
|
|
71
|
+
(0, _vitest.describe)('Pagination — mo 모드 이전/다음 버튼 노출', () => {
|
|
72
|
+
(0, _vitest.it)('페이지가 그룹 크기 이하여도 mo 에서는 이전·다음 버튼이 노출된다', () => {
|
|
73
|
+
// Given/When: 3페이지(그룹 크기 10 이하) 분량을 mo 로 렌더하면
|
|
74
|
+
const el = render({
|
|
75
|
+
totalCount: TOTAL_COUNT_3_PAGES,
|
|
76
|
+
itemCountPerPage: ITEM_COUNT_PER_PAGE,
|
|
77
|
+
pageCount: PAGE_GROUP_SIZE,
|
|
78
|
+
currentPage: 1,
|
|
79
|
+
breakPoint: 'mo'
|
|
80
|
+
});
|
|
81
|
+
// Then: mo 는 페이지 번호 목록이 CSS 로 숨겨지므로 이전·다음 버튼이 있어야 이동할 수 있다
|
|
82
|
+
(0, _vitest.expect)(prevOf(el)).not.toBeNull();
|
|
83
|
+
(0, _vitest.expect)(nextOf(el)).not.toBeNull();
|
|
84
|
+
});
|
|
85
|
+
(0, _vitest.it)('첫 페이지에서는 이전 버튼이, 마지막 페이지에서는 다음 버튼이 비활성이다', () => {
|
|
86
|
+
// Given/When: 3페이지 중 첫 페이지를 mo 로 렌더하면
|
|
87
|
+
const first = render({
|
|
88
|
+
totalCount: TOTAL_COUNT_3_PAGES,
|
|
89
|
+
itemCountPerPage: ITEM_COUNT_PER_PAGE,
|
|
90
|
+
pageCount: PAGE_GROUP_SIZE,
|
|
91
|
+
currentPage: 1,
|
|
92
|
+
breakPoint: 'mo'
|
|
93
|
+
});
|
|
94
|
+
// Then: 이전만 막히고 다음은 누를 수 있다
|
|
95
|
+
(0, _vitest.expect)(prevOf(first)?.disabled).toBe(true);
|
|
96
|
+
(0, _vitest.expect)(nextOf(first)?.disabled).toBe(false);
|
|
97
|
+
// When: 마지막 페이지로 이동하면
|
|
98
|
+
const last = render({
|
|
99
|
+
totalCount: TOTAL_COUNT_3_PAGES,
|
|
100
|
+
itemCountPerPage: ITEM_COUNT_PER_PAGE,
|
|
101
|
+
pageCount: PAGE_GROUP_SIZE,
|
|
102
|
+
currentPage: LAST_PAGE_3,
|
|
103
|
+
breakPoint: 'mo'
|
|
104
|
+
});
|
|
105
|
+
// Then: 반대로 다음이 막힌다
|
|
106
|
+
(0, _vitest.expect)(prevOf(last)?.disabled).toBe(false);
|
|
107
|
+
(0, _vitest.expect)(nextOf(last)?.disabled).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
(0, _vitest.it)('페이지가 하나뿐이면 mo 에서도 이전·다음 버튼을 내보내지 않는다', () => {
|
|
110
|
+
// Given/When: 1페이지 분량을 mo 로 렌더하면
|
|
111
|
+
const el = render({
|
|
112
|
+
totalCount: TOTAL_COUNT_1_PAGE,
|
|
113
|
+
itemCountPerPage: ITEM_COUNT_PER_PAGE,
|
|
114
|
+
pageCount: PAGE_GROUP_SIZE,
|
|
115
|
+
currentPage: 1,
|
|
116
|
+
breakPoint: 'mo'
|
|
117
|
+
});
|
|
118
|
+
// Then: 이동할 곳이 없으므로 버튼도 없다
|
|
119
|
+
(0, _vitest.expect)(prevOf(el)).toBeNull();
|
|
120
|
+
(0, _vitest.expect)(nextOf(el)).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
(0, _vitest.it)('pc 는 기존대로 페이지 그룹을 넘길 때만 점프 버튼을 노출한다', () => {
|
|
123
|
+
// Given/When: 3페이지 분량을 pc 로 렌더하면
|
|
124
|
+
const el = render({
|
|
125
|
+
totalCount: TOTAL_COUNT_3_PAGES,
|
|
126
|
+
itemCountPerPage: ITEM_COUNT_PER_PAGE,
|
|
127
|
+
pageCount: PAGE_GROUP_SIZE,
|
|
128
|
+
currentPage: 1,
|
|
129
|
+
breakPoint: 'pc'
|
|
130
|
+
});
|
|
131
|
+
// Then: 페이지 번호로 직접 이동하므로 점프 버튼은 나오지 않는다
|
|
132
|
+
(0, _vitest.expect)(prevOf(el)).toBeNull();
|
|
133
|
+
(0, _vitest.expect)(nextOf(el)).toBeNull();
|
|
134
|
+
(0, _vitest.expect)(pageNums(el)).toHaveLength(LAST_PAGE_3);
|
|
135
|
+
});
|
|
62
136
|
});
|
|
@@ -8,16 +8,21 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
8
8
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
9
9
|
var _react = require("react");
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
const WIDTH_CLASS = {
|
|
12
|
+
full: 'ncua-callout-notification--full',
|
|
13
|
+
fit: 'ncua-callout-notification--fit'
|
|
14
|
+
};
|
|
11
15
|
const CalloutNotification = exports.CalloutNotification = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
12
16
|
let {
|
|
13
17
|
color = 'neutral',
|
|
14
18
|
className,
|
|
15
19
|
title,
|
|
20
|
+
width,
|
|
16
21
|
...rest
|
|
17
22
|
} = _ref;
|
|
18
23
|
return (0, _jsxRuntime.jsx)("div", {
|
|
19
24
|
ref: ref,
|
|
20
|
-
className: (0, _classnames.default)('ncua-callout-notification', `ncua-callout-notification--${color}`, className),
|
|
25
|
+
className: (0, _classnames.default)('ncua-callout-notification', `ncua-callout-notification--${color}`, width && WIDTH_CLASS[width], className),
|
|
21
26
|
...rest,
|
|
22
27
|
children: title
|
|
23
28
|
});
|
|
@@ -10,10 +10,15 @@ var _CalloutNotification = require("./CalloutNotification");
|
|
|
10
10
|
var _FloatingNotification = require("./FloatingNotification");
|
|
11
11
|
var _FullWidthNotification = require("./FullWidthNotification");
|
|
12
12
|
var _MessageNotification = require("./MessageNotification");
|
|
13
|
-
const Notification = exports.Notification = /*#__PURE__*/(0, _react.forwardRef)(
|
|
13
|
+
const Notification = exports.Notification = /*#__PURE__*/(0, _react.forwardRef)(
|
|
14
|
+
// 단일 type 에만 유효한 prop 은 rest 에서 분리해 해당 분기로만 명시 전달한다.
|
|
15
|
+
// rest 에 남기면 다른 type 의 하위 컴포넌트가 div 로 스프레드해 DOM 속성으로 출력된다.
|
|
16
|
+
(_ref, ref) => {
|
|
14
17
|
let {
|
|
15
18
|
type = 'floating',
|
|
16
19
|
color = 'neutral',
|
|
20
|
+
width,
|
|
21
|
+
supportTextLink,
|
|
17
22
|
...rest
|
|
18
23
|
} = _ref;
|
|
19
24
|
if (type === 'floating') {
|
|
@@ -26,6 +31,7 @@ const Notification = exports.Notification = /*#__PURE__*/(0, _react.forwardRef)(
|
|
|
26
31
|
if (type === 'full-width') {
|
|
27
32
|
return (0, _jsxRuntime.jsx)(_FullWidthNotification.FullWidthNotification, {
|
|
28
33
|
color: color,
|
|
34
|
+
supportTextLink: supportTextLink,
|
|
29
35
|
...rest,
|
|
30
36
|
ref: ref
|
|
31
37
|
});
|
|
@@ -38,9 +44,23 @@ const Notification = exports.Notification = /*#__PURE__*/(0, _react.forwardRef)(
|
|
|
38
44
|
});
|
|
39
45
|
}
|
|
40
46
|
if (type === 'callout') {
|
|
47
|
+
// 나머지 3개 type 이 공유하는 prop 은 callout 이 지원하지 않으므로 여기서 제외한다.
|
|
48
|
+
const {
|
|
49
|
+
icon: _icon,
|
|
50
|
+
supportingText: _supportingText,
|
|
51
|
+
actions: _actions,
|
|
52
|
+
autoClose: _autoClose,
|
|
53
|
+
onClose: _onClose,
|
|
54
|
+
onHidePermanently: _onHidePermanently,
|
|
55
|
+
hidePermanentlyLabel: _hidePermanentlyLabel,
|
|
56
|
+
portal: _portal,
|
|
57
|
+
...calloutProps
|
|
58
|
+
} = rest;
|
|
41
59
|
return (0, _jsxRuntime.jsx)(_CalloutNotification.CalloutNotification, {
|
|
42
60
|
color: color,
|
|
43
|
-
|
|
61
|
+
width: width,
|
|
62
|
+
...calloutProps,
|
|
63
|
+
ref: ref
|
|
44
64
|
});
|
|
45
65
|
}
|
|
46
66
|
return null;
|
|
@@ -0,0 +1,128 @@
|
|
|
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 _CalloutNotification = require("../CalloutNotification");
|
|
8
|
+
var _Notification = require("../Notification");
|
|
9
|
+
// @vitest-environment jsdom
|
|
10
|
+
|
|
11
|
+
// React 18에서 act() 사용 시 필요한 플래그 (미설정 시 console.error 경고 발생)
|
|
12
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
13
|
+
const BASE = 'ncua-callout-notification';
|
|
14
|
+
let root = null;
|
|
15
|
+
let container = null;
|
|
16
|
+
const mount = element => {
|
|
17
|
+
container = document.createElement('div');
|
|
18
|
+
document.body.appendChild(container);
|
|
19
|
+
root = (0, _client.createRoot)(container);
|
|
20
|
+
(0, _testUtils.act)(() => {
|
|
21
|
+
root?.render(element);
|
|
22
|
+
});
|
|
23
|
+
const rendered = container.firstElementChild;
|
|
24
|
+
if (!(rendered instanceof HTMLElement)) throw new Error('callout 이 렌더되지 않았다');
|
|
25
|
+
return rendered;
|
|
26
|
+
};
|
|
27
|
+
const renderCallout = function () {
|
|
28
|
+
let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
29
|
+
return mount(/*#__PURE__*/(0, _react.createElement)(_CalloutNotification.CalloutNotification, {
|
|
30
|
+
title: '안내 문구',
|
|
31
|
+
...props
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
const renderNotification = function () {
|
|
35
|
+
let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
36
|
+
return mount(/*#__PURE__*/(0, _react.createElement)(_Notification.Notification, {
|
|
37
|
+
type: 'callout',
|
|
38
|
+
title: '안내 문구',
|
|
39
|
+
...props
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
(0, _vitest.afterEach)(() => {
|
|
43
|
+
(0, _testUtils.act)(() => {
|
|
44
|
+
root?.unmount();
|
|
45
|
+
});
|
|
46
|
+
container?.remove();
|
|
47
|
+
root = null;
|
|
48
|
+
container = null;
|
|
49
|
+
});
|
|
50
|
+
(0, _vitest.describe)('CalloutNotification — width prop', () => {
|
|
51
|
+
(0, _vitest.it)('width 미지정 시 너비 클래스를 붙이지 않는다 (부모 너비를 채우는 기본 스타일 유지)', () => {
|
|
52
|
+
const element = renderCallout();
|
|
53
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--full')).toBe(false);
|
|
54
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--fit')).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
(0, _vitest.it)("width='full' 은 --full modifier 를 붙인다", () => {
|
|
57
|
+
const element = renderCallout({
|
|
58
|
+
width: 'full'
|
|
59
|
+
});
|
|
60
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--full')).toBe(true);
|
|
61
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--fit')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
(0, _vitest.it)("width='fit' 은 --fit modifier 를 붙인다", () => {
|
|
64
|
+
const element = renderCallout({
|
|
65
|
+
width: 'fit'
|
|
66
|
+
});
|
|
67
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--fit')).toBe(true);
|
|
68
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--full')).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
(0, _vitest.it)('width 는 DOM 속성으로 새지 않는다', () => {
|
|
71
|
+
const element = renderCallout({
|
|
72
|
+
width: 'fit'
|
|
73
|
+
});
|
|
74
|
+
(0, _vitest.expect)(element.hasAttribute('width')).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
(0, _vitest.it)('width 는 베이스/컬러 클래스와 외부 className 을 유지한 채 추가된다', () => {
|
|
77
|
+
const element = renderCallout({
|
|
78
|
+
width: 'fit',
|
|
79
|
+
color: 'info',
|
|
80
|
+
className: 'custom'
|
|
81
|
+
});
|
|
82
|
+
(0, _vitest.expect)(element.classList.contains(BASE)).toBe(true);
|
|
83
|
+
(0, _vitest.expect)(element.classList.contains(`${BASE}--info`)).toBe(true);
|
|
84
|
+
(0, _vitest.expect)(element.classList.contains('custom')).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
(0, _vitest.describe)('Notification type="callout" — 위임 계약', () => {
|
|
88
|
+
(0, _vitest.it)('width 를 CalloutNotification 으로 전달한다', () => {
|
|
89
|
+
const element = renderNotification({
|
|
90
|
+
width: 'fit'
|
|
91
|
+
});
|
|
92
|
+
(0, _vitest.expect)(element.classList.contains(BASE)).toBe(true);
|
|
93
|
+
(0, _vitest.expect)(element.classList.contains('ncua-callout-notification--fit')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
// Notification 의 callout 분기가 제외하는 prop 전체. 여기 누락된 prop 은 DOM 속성으로 출력된다.
|
|
96
|
+
(0, _vitest.it)('callout 이 지원하지 않는 prop 은 DOM 속성으로 새지 않는다', () => {
|
|
97
|
+
const element = renderNotification({
|
|
98
|
+
icon: () => null,
|
|
99
|
+
supportingText: '보조 문구',
|
|
100
|
+
supportTextLink: 'https://example.com',
|
|
101
|
+
actions: [{
|
|
102
|
+
label: '확인',
|
|
103
|
+
onClick: () => undefined
|
|
104
|
+
}],
|
|
105
|
+
autoClose: 3000,
|
|
106
|
+
portal: true,
|
|
107
|
+
onClose: () => undefined,
|
|
108
|
+
onHidePermanently: () => undefined,
|
|
109
|
+
hidePermanentlyLabel: '다시 보지 않기'
|
|
110
|
+
});
|
|
111
|
+
for (const attr of ['icon', 'supportingtext', 'supporttextlink', 'actions', 'autoclose', 'portal', 'onclose', 'onhidepermanently', 'hidepermanentlylabel']) {
|
|
112
|
+
(0, _vitest.expect)(element.hasAttribute(attr), attr).toBe(false);
|
|
113
|
+
}
|
|
114
|
+
(0, _vitest.expect)(element.textContent).toBe('안내 문구');
|
|
115
|
+
});
|
|
116
|
+
(0, _vitest.it)('ref 를 callout 루트 엘리먼트로 전달한다', () => {
|
|
117
|
+
let captured = null;
|
|
118
|
+
mount(/*#__PURE__*/(0, _react.createElement)(_Notification.Notification, {
|
|
119
|
+
type: 'callout',
|
|
120
|
+
title: '안내 문구',
|
|
121
|
+
ref: node => {
|
|
122
|
+
captured = node;
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
(0, _vitest.expect)(captured).not.toBeNull();
|
|
126
|
+
(0, _vitest.expect)(captured.classList.contains(BASE)).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
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 _Notification = require("../Notification");
|
|
8
|
+
// @vitest-environment jsdom
|
|
9
|
+
|
|
10
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
11
|
+
let root = null;
|
|
12
|
+
let container = null;
|
|
13
|
+
(0, _vitest.beforeAll)(() => {
|
|
14
|
+
// jsdom 미구현 API 보정 — FloatingNotification 의 모바일 리스너가 window.matchMedia 를 호출한다.
|
|
15
|
+
if (typeof window.matchMedia !== 'function') {
|
|
16
|
+
const stub = query => ({
|
|
17
|
+
matches: false,
|
|
18
|
+
media: query,
|
|
19
|
+
onchange: null,
|
|
20
|
+
addEventListener: () => undefined,
|
|
21
|
+
removeEventListener: () => undefined,
|
|
22
|
+
addListener: () => undefined,
|
|
23
|
+
removeListener: () => undefined,
|
|
24
|
+
dispatchEvent: () => false
|
|
25
|
+
});
|
|
26
|
+
window.matchMedia = stub;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
// 정의되지 않은 type 의 런타임 동작까지 고정하므로 props 를 넓게 받는다.
|
|
30
|
+
const render = props => {
|
|
31
|
+
container = document.createElement('div');
|
|
32
|
+
document.body.appendChild(container);
|
|
33
|
+
root = (0, _client.createRoot)(container);
|
|
34
|
+
(0, _testUtils.act)(() => {
|
|
35
|
+
root?.render(/*#__PURE__*/(0, _react.createElement)(_Notification.Notification, {
|
|
36
|
+
title: '안내 문구',
|
|
37
|
+
...props
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
const rendered = container.firstElementChild;
|
|
41
|
+
return rendered instanceof HTMLElement ? rendered : null;
|
|
42
|
+
};
|
|
43
|
+
(0, _vitest.afterEach)(() => {
|
|
44
|
+
(0, _testUtils.act)(() => {
|
|
45
|
+
root?.unmount();
|
|
46
|
+
});
|
|
47
|
+
container?.remove();
|
|
48
|
+
root = null;
|
|
49
|
+
container = null;
|
|
50
|
+
});
|
|
51
|
+
(0, _vitest.describe)('Notification — type 별 위임', () => {
|
|
52
|
+
_vitest.it.each([['floating', 'ncua-floating-notification'], ['full-width', 'ncua-full-width-notification'], ['message', 'ncua-message-notification'], ['callout', 'ncua-callout-notification']])('type=%s 는 %s 를 렌더한다', (type, baseClass) => {
|
|
53
|
+
const element = render({
|
|
54
|
+
type
|
|
55
|
+
});
|
|
56
|
+
(0, _vitest.expect)(element?.classList.contains(baseClass)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
(0, _vitest.it)('type 미지정은 floating 으로 렌더한다', () => {
|
|
59
|
+
const element = render({});
|
|
60
|
+
(0, _vitest.expect)(element?.classList.contains('ncua-floating-notification')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
_vitest.it.each(['floating', 'full-width', 'message'])('type=%s 에 callout 전용 width 를 주어도 DOM 속성으로 새지 않는다', type => {
|
|
63
|
+
const element = render({
|
|
64
|
+
type,
|
|
65
|
+
width: 'fit'
|
|
66
|
+
});
|
|
67
|
+
(0, _vitest.expect)(element?.hasAttribute('width')).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
_vitest.it.each(['floating', 'message'])('type=%s 에 full-width 전용 supportTextLink 를 주어도 DOM 속성으로 새지 않는다', type => {
|
|
70
|
+
const element = render({
|
|
71
|
+
type,
|
|
72
|
+
supportingText: '보조',
|
|
73
|
+
supportTextLink: 'https://example.com'
|
|
74
|
+
});
|
|
75
|
+
(0, _vitest.expect)(element?.hasAttribute('supporttextlink')).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
(0, _vitest.it)('정의되지 않은 type 은 아무것도 렌더하지 않는다', () => {
|
|
78
|
+
const element = render({
|
|
79
|
+
type: 'banner'
|
|
80
|
+
});
|
|
81
|
+
(0, _vitest.expect)(element).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
});
|