@ncds/ui-admin 1.8.21-alpha.1 → 1.8.21-alpha.10

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 (125) hide show
  1. package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +17 -6
  2. package/dist/cjs/assets/scripts/notification/MessageNotification.js +10 -48
  3. package/dist/cjs/assets/scripts/notification/const/classNames.js +3 -0
  4. package/dist/cjs/assets/scripts/notification/const/index.js +1 -1
  5. package/dist/cjs/assets/scripts/notification/const/sizes.js +4 -2
  6. package/dist/cjs/assets/scripts/notification/messageTemplate.js +106 -0
  7. package/dist/cjs/assets/scripts/notification/utils.js +40 -4
  8. package/dist/cjs/constant/notification.js +21 -0
  9. package/dist/cjs/src/components/forms-and-input/input-base/InputBase.js +30 -18
  10. package/dist/cjs/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +116 -0
  11. package/dist/cjs/src/components/forms-and-input/password-input/PasswordInput.js +3 -3
  12. package/dist/cjs/src/components/forms-and-input/password-input/__tests__/PasswordInput.test.js +59 -0
  13. package/dist/cjs/src/components/index.js +11 -0
  14. package/dist/cjs/src/components/layout/ncua-scope/NcuaScope.js +8 -2
  15. package/dist/cjs/src/components/navigation/pagination/Pagination.js +3 -1
  16. package/dist/cjs/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
  17. package/dist/cjs/src/components/overlays/notification/FullWidthNotification.js +3 -1
  18. package/dist/cjs/src/components/overlays/notification/MessageNotification.js +14 -54
  19. package/dist/cjs/src/components/overlays/notification/MessageNotificationParts.js +92 -0
  20. package/dist/cjs/src/components/overlays/notification/__tests__/MessageNotification.test.js +246 -0
  21. package/dist/cjs/src/components/overlays/notification/__tests__/hidePermanentlyLabel.test.js +209 -0
  22. package/dist/cjs/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +259 -0
  23. package/dist/cjs/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +70 -0
  24. package/dist/cjs/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +623 -0
  25. package/dist/cjs/src/components/overlays/postcode-search-modal/index.js +16 -0
  26. package/dist/cjs/src/generated/scoped-css.js +1 -1
  27. package/dist/esm/assets/scripts/notification/FullWidthNotification.js +17 -6
  28. package/dist/esm/assets/scripts/notification/MessageNotification.js +12 -50
  29. package/dist/esm/assets/scripts/notification/const/classNames.js +3 -0
  30. package/dist/esm/assets/scripts/notification/const/index.js +3 -2
  31. package/dist/esm/assets/scripts/notification/const/sizes.js +4 -2
  32. package/dist/esm/assets/scripts/notification/messageTemplate.js +100 -0
  33. package/dist/esm/assets/scripts/notification/utils.js +39 -4
  34. package/dist/esm/constant/notification.js +15 -0
  35. package/dist/esm/src/components/forms-and-input/input-base/InputBase.js +30 -18
  36. package/dist/esm/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +116 -0
  37. package/dist/esm/src/components/forms-and-input/password-input/PasswordInput.js +3 -3
  38. package/dist/esm/src/components/forms-and-input/password-input/__tests__/PasswordInput.test.js +56 -0
  39. package/dist/esm/src/components/index.js +1 -0
  40. package/dist/esm/src/components/layout/ncua-scope/NcuaScope.js +8 -2
  41. package/dist/esm/src/components/navigation/pagination/Pagination.js +3 -1
  42. package/dist/esm/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
  43. package/dist/esm/src/components/overlays/notification/FullWidthNotification.js +3 -1
  44. package/dist/esm/src/components/overlays/notification/MessageNotification.js +15 -55
  45. package/dist/esm/src/components/overlays/notification/MessageNotificationParts.js +85 -0
  46. package/dist/esm/src/components/overlays/notification/__tests__/MessageNotification.test.js +243 -0
  47. package/dist/esm/src/components/overlays/notification/__tests__/hidePermanentlyLabel.test.js +206 -0
  48. package/dist/esm/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +252 -0
  49. package/dist/esm/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +63 -0
  50. package/dist/esm/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +620 -0
  51. package/dist/esm/src/components/overlays/postcode-search-modal/index.js +1 -0
  52. package/dist/esm/src/generated/scoped-css.js +1 -1
  53. package/dist/temp/assets/scripts/notification/FullWidthNotification.d.ts +6 -0
  54. package/dist/temp/assets/scripts/notification/FullWidthNotification.js +14 -5
  55. package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +0 -3
  56. package/dist/temp/assets/scripts/notification/MessageNotification.js +9 -44
  57. package/dist/temp/assets/scripts/notification/const/classNames.d.ts +1 -0
  58. package/dist/temp/assets/scripts/notification/const/classNames.js +3 -0
  59. package/dist/temp/assets/scripts/notification/const/index.d.ts +3 -3
  60. package/dist/temp/assets/scripts/notification/const/index.js +3 -2
  61. package/dist/temp/assets/scripts/notification/const/sizes.d.ts +2 -2
  62. package/dist/temp/assets/scripts/notification/const/sizes.js +4 -2
  63. package/dist/temp/assets/scripts/notification/const/types.d.ts +30 -2
  64. package/dist/temp/assets/scripts/notification/messageTemplate.d.ts +15 -0
  65. package/dist/temp/assets/scripts/notification/messageTemplate.js +79 -0
  66. package/dist/temp/assets/scripts/notification/utils.d.ts +19 -2
  67. package/dist/temp/assets/scripts/notification/utils.js +39 -5
  68. package/dist/temp/constant/notification.d.ts +15 -0
  69. package/dist/temp/constant/notification.js +15 -0
  70. package/dist/temp/src/components/forms-and-input/input-base/InputBase.d.ts +1 -1
  71. package/dist/temp/src/components/forms-and-input/input-base/InputBase.js +26 -14
  72. package/dist/temp/src/components/forms-and-input/input-base/__tests__/InputBase.test.js +79 -0
  73. package/dist/temp/src/components/forms-and-input/password-input/PasswordInput.js +2 -2
  74. package/dist/temp/src/components/forms-and-input/password-input/__tests__/PasswordInput.test.d.ts +1 -0
  75. package/dist/temp/src/components/forms-and-input/password-input/__tests__/PasswordInput.test.js +48 -0
  76. package/dist/temp/src/components/index.d.ts +1 -0
  77. package/dist/temp/src/components/index.js +1 -0
  78. package/dist/temp/src/components/layout/ncua-scope/NcuaScope.js +5 -1
  79. package/dist/temp/src/components/navigation/pagination/Pagination.js +3 -1
  80. package/dist/temp/src/components/navigation/pagination/__tests__/Pagination.test.js +74 -0
  81. package/dist/temp/src/components/overlays/notification/FullWidthNotification.d.ts +6 -0
  82. package/dist/temp/src/components/overlays/notification/FullWidthNotification.js +3 -2
  83. package/dist/temp/src/components/overlays/notification/MessageNotification.d.ts +6 -0
  84. package/dist/temp/src/components/overlays/notification/MessageNotification.js +4 -13
  85. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.d.ts +28 -0
  86. package/dist/temp/src/components/overlays/notification/MessageNotificationParts.js +28 -0
  87. package/dist/temp/src/components/overlays/notification/Notification.d.ts +11 -2
  88. package/dist/temp/src/components/overlays/notification/__tests__/MessageNotification.test.d.ts +1 -0
  89. package/dist/temp/src/components/overlays/notification/__tests__/MessageNotification.test.js +190 -0
  90. package/dist/temp/src/components/overlays/notification/__tests__/hidePermanentlyLabel.test.d.ts +1 -0
  91. package/dist/temp/src/components/overlays/notification/__tests__/hidePermanentlyLabel.test.js +160 -0
  92. package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModal.d.ts +45 -0
  93. package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModal.js +125 -0
  94. package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.d.ts +16 -0
  95. package/dist/temp/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.js +24 -0
  96. package/dist/temp/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.d.ts +1 -0
  97. package/dist/temp/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.js +503 -0
  98. package/dist/temp/src/components/overlays/postcode-search-modal/index.d.ts +1 -0
  99. package/dist/temp/src/components/overlays/postcode-search-modal/index.js +1 -0
  100. package/dist/temp/src/generated/scoped-css.js +1 -1
  101. package/dist/types/assets/scripts/notification/FullWidthNotification.d.ts +6 -0
  102. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +0 -3
  103. package/dist/types/assets/scripts/notification/const/classNames.d.ts +1 -0
  104. package/dist/types/assets/scripts/notification/const/index.d.ts +3 -3
  105. package/dist/types/assets/scripts/notification/const/sizes.d.ts +2 -2
  106. package/dist/types/assets/scripts/notification/const/types.d.ts +30 -2
  107. package/dist/types/assets/scripts/notification/messageTemplate.d.ts +15 -0
  108. package/dist/types/assets/scripts/notification/utils.d.ts +19 -2
  109. package/dist/types/constant/notification.d.ts +15 -0
  110. package/dist/types/src/components/forms-and-input/input-base/InputBase.d.ts +1 -1
  111. package/dist/types/src/components/forms-and-input/password-input/__tests__/PasswordInput.test.d.ts +1 -0
  112. package/dist/types/src/components/index.d.ts +1 -0
  113. package/dist/types/src/components/overlays/notification/FullWidthNotification.d.ts +6 -0
  114. package/dist/types/src/components/overlays/notification/MessageNotification.d.ts +6 -0
  115. package/dist/types/src/components/overlays/notification/MessageNotificationParts.d.ts +28 -0
  116. package/dist/types/src/components/overlays/notification/Notification.d.ts +11 -2
  117. package/dist/types/src/components/overlays/notification/__tests__/MessageNotification.test.d.ts +1 -0
  118. package/dist/types/src/components/overlays/notification/__tests__/hidePermanentlyLabel.test.d.ts +1 -0
  119. package/dist/types/src/components/overlays/postcode-search-modal/PostcodeSearchModal.d.ts +45 -0
  120. package/dist/types/src/components/overlays/postcode-search-modal/PostcodeSearchModalTip.d.ts +16 -0
  121. package/dist/types/src/components/overlays/postcode-search-modal/__tests__/PostcodeSearchModal.test.d.ts +1 -0
  122. package/dist/types/src/components/overlays/postcode-search-modal/index.d.ts +1 -0
  123. package/dist/ui-admin/assets/styles/style.css +280 -35
  124. package/dist/ui-admin/assets/styles/style.scoped.css +280 -35
  125. package/package.json +1 -1
@@ -0,0 +1,209 @@
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 _FullWidthNotification = require("../FullWidthNotification");
8
+ var _MessageNotification = require("../MessageNotification");
9
+ var _Notification = require("../Notification");
10
+ // @vitest-environment jsdom
11
+
12
+ // React 18에서 act() 사용 시 필요한 플래그 (미설정 시 console.error 경고 발생)
13
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
14
+ const SELECTOR = {
15
+ /** message 타입 — 고도몰이 textContent 덮어쓰기에 쓰는 하위호환 계약 선택자. */
16
+ MESSAGE_HIDE_LINK: '.ncua-message-notification__hide-link',
17
+ /** full-width 타입 — 다시보지 않기 버튼. */
18
+ FULL_WIDTH_LINK: '.ncua-full-width-notification__link',
19
+ ACTION_BUTTON: '.ncua-notification__action-button'
20
+ };
21
+ /** 각 타입의 현행 기본 문구. 표기 통일은 별도 결정 사항이므로 서로 다른 값을 그대로 유지한다. */
22
+ const DEFAULT_LABEL = {
23
+ MESSAGE: '다시 보지 않기',
24
+ FULL_WIDTH: '다시보지 않기'
25
+ };
26
+ const mounted = [];
27
+ function render(element) {
28
+ const container = document.createElement('div');
29
+ document.body.appendChild(container);
30
+ const root = (0, _client.createRoot)(container);
31
+ (0, _testUtils.act)(() => {
32
+ root.render(element);
33
+ });
34
+ mounted.push({
35
+ root,
36
+ container
37
+ });
38
+ return container;
39
+ }
40
+ function renderMessage() {
41
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
42
+ return render(/*#__PURE__*/(0, _react.createElement)(_MessageNotification.MessageNotification, {
43
+ title: '알림 제목',
44
+ ...props
45
+ }));
46
+ }
47
+ function renderFullWidth() {
48
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
49
+ return render(/*#__PURE__*/(0, _react.createElement)(_FullWidthNotification.FullWidthNotification, {
50
+ title: '알림 제목',
51
+ ...props
52
+ }));
53
+ }
54
+ function renderNotification() {
55
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
56
+ return render(/*#__PURE__*/(0, _react.createElement)(_Notification.Notification, {
57
+ title: '알림 제목',
58
+ ...props
59
+ }));
60
+ }
61
+ (0, _vitest.afterEach)(() => {
62
+ for (const {
63
+ root,
64
+ container
65
+ } of mounted.splice(0)) {
66
+ (0, _testUtils.act)(() => {
67
+ root.unmount();
68
+ });
69
+ container.remove();
70
+ }
71
+ });
72
+ (0, _vitest.describe)('hidePermanentlyLabel (React) — message 타입', () => {
73
+ (0, _vitest.it)('R-1: prop 미지정 시 기본 문구 "다시 보지 않기"가 렌더된다', () => {
74
+ // Given: hidePermanentlyLabel 없이 onHidePermanently만 주어지고
75
+ // When: MessageNotification을 렌더하면
76
+ const container = renderMessage({
77
+ onHidePermanently: _vitest.vi.fn()
78
+ });
79
+ // Then: 현행 기본 문구가 그대로 노출된다 (시각적 변화 0)
80
+ const hideLink = container.querySelector(SELECTOR.MESSAGE_HIDE_LINK);
81
+ (0, _vitest.expect)(hideLink?.textContent).toBe(DEFAULT_LABEL.MESSAGE);
82
+ });
83
+ (0, _vitest.it)('R-2: prop 지정 시 그 값이 렌더된다', () => {
84
+ // Given: 커스텀 라벨이 주어지고
85
+ // When: 렌더하면
86
+ const container = renderMessage({
87
+ onHidePermanently: _vitest.vi.fn(),
88
+ hidePermanentlyLabel: '30일간 보지 않기'
89
+ });
90
+ // Then: 하드코딩 문구 대신 전달한 값이 노출된다
91
+ const hideLink = container.querySelector(SELECTOR.MESSAGE_HIDE_LINK);
92
+ (0, _vitest.expect)(hideLink?.textContent).toBe('30일간 보지 않기');
93
+ });
94
+ (0, _vitest.it)('R-3: ReactNode 라벨을 받아 엘리먼트로 렌더한다', () => {
95
+ // Given: 문자열이 아닌 ReactNode 라벨이 주어지고
96
+ const label = /*#__PURE__*/(0, _react.createElement)('span', {
97
+ className: 'custom-hide-label'
98
+ }, '커스텀 노드');
99
+ // When: 렌더하면
100
+ const container = renderMessage({
101
+ onHidePermanently: _vitest.vi.fn(),
102
+ hidePermanentlyLabel: label
103
+ });
104
+ // Then: 노드가 hide-link 내부에 그대로 마운트된다
105
+ const hideLink = container.querySelector(SELECTOR.MESSAGE_HIDE_LINK);
106
+ (0, _vitest.expect)(hideLink?.querySelector('.custom-hide-label')?.textContent).toBe('커스텀 노드');
107
+ });
108
+ (0, _vitest.it)('R-4: 라벨을 바꿔도 __hide-link 클래스 계약이 유지된다', () => {
109
+ // Given: 커스텀 라벨이 주어지고
110
+ // When: 렌더하면
111
+ const container = renderMessage({
112
+ onHidePermanently: _vitest.vi.fn(),
113
+ hidePermanentlyLabel: '커스텀'
114
+ });
115
+ // Then: 고도몰의 textContent 덮어쓰기가 계속 동작하도록 기존 클래스가 모두 남는다
116
+ const hideLink = container.querySelector(SELECTOR.MESSAGE_HIDE_LINK);
117
+ (0, _vitest.expect)(hideLink).not.toBeNull();
118
+ (0, _vitest.expect)(hideLink?.classList.contains('ncua-notification__action-button')).toBe(true);
119
+ (0, _vitest.expect)(hideLink?.classList.contains('ncua-message-notification__hide-link')).toBe(true);
120
+ });
121
+ (0, _vitest.it)('R-5: onHidePermanently 없이 라벨만 주면 버튼이 렌더되지 않는다', () => {
122
+ // Given: 핸들러 없이 라벨만 주어지고
123
+ // When: 렌더하면
124
+ const container = renderMessage({
125
+ hidePermanentlyLabel: '커스텀'
126
+ });
127
+ // Then: 라벨만으로는 버튼이 생기지 않는다 (핸들러가 렌더 조건)
128
+ (0, _vitest.expect)(container.querySelector(SELECTOR.MESSAGE_HIDE_LINK)).toBeNull();
129
+ });
130
+ (0, _vitest.it)('R-6: 라벨이 DOM 속성으로 새어나가지 않는다', () => {
131
+ // Given: 커스텀 라벨이 주어지고
132
+ // When: 렌더하면
133
+ const container = renderMessage({
134
+ onHidePermanently: _vitest.vi.fn(),
135
+ hidePermanentlyLabel: '커스텀'
136
+ });
137
+ // Then: rest 스프레드를 타고 루트 div의 속성으로 흘러들지 않는다
138
+ const root = container.querySelector('.ncua-message-notification');
139
+ (0, _vitest.expect)(root?.hasAttribute('hidepermanentlylabel')).toBe(false);
140
+ });
141
+ });
142
+ (0, _vitest.describe)('hidePermanentlyLabel (React) — full-width 타입', () => {
143
+ (0, _vitest.it)('R-7: prop 미지정 시 기본 문구 "다시보지 않기"가 렌더된다', () => {
144
+ // Given: hidePermanentlyLabel 없이 onHidePermanently만 주어지고
145
+ // When: FullWidthNotification을 렌더하면
146
+ const container = renderFullWidth({
147
+ onHidePermanently: _vitest.vi.fn()
148
+ });
149
+ // Then: message 타입과 다른 현행 문구가 그대로 유지된다 (표기 통일은 별도 결정)
150
+ const hideButton = container.querySelector(SELECTOR.FULL_WIDTH_LINK);
151
+ (0, _vitest.expect)(hideButton?.textContent).toBe(DEFAULT_LABEL.FULL_WIDTH);
152
+ });
153
+ (0, _vitest.it)('R-8: prop 지정 시 그 값이 렌더된다', () => {
154
+ // Given: 커스텀 라벨이 주어지고
155
+ // When: 렌더하면
156
+ const container = renderFullWidth({
157
+ onHidePermanently: _vitest.vi.fn(),
158
+ hidePermanentlyLabel: '오늘 하루 보지 않기'
159
+ });
160
+ // Then: 전달한 값이 노출된다
161
+ const hideButton = container.querySelector(SELECTOR.FULL_WIDTH_LINK);
162
+ (0, _vitest.expect)(hideButton?.textContent).toBe('오늘 하루 보지 않기');
163
+ });
164
+ (0, _vitest.it)('R-9: 라벨을 바꿔도 기존 클래스 계약이 유지된다', () => {
165
+ // Given: 커스텀 라벨이 주어지고
166
+ // When: 렌더하면
167
+ const container = renderFullWidth({
168
+ onHidePermanently: _vitest.vi.fn(),
169
+ hidePermanentlyLabel: '커스텀'
170
+ });
171
+ // Then: 액션 버튼 클래스가 그대로 남는다
172
+ const hideButton = container.querySelector(SELECTOR.FULL_WIDTH_LINK);
173
+ (0, _vitest.expect)(hideButton?.classList.contains('ncua-notification__action-button')).toBe(true);
174
+ (0, _vitest.expect)(hideButton?.classList.contains('ncua-notification__action-button--text')).toBe(true);
175
+ });
176
+ (0, _vitest.it)('R-10: onHidePermanently 없이 라벨만 주면 버튼이 렌더되지 않는다', () => {
177
+ // Given: 핸들러 없이 라벨만 주어지고
178
+ // When: 렌더하면
179
+ const container = renderFullWidth({
180
+ hidePermanentlyLabel: '커스텀'
181
+ });
182
+ // Then: 액션 버튼이 생기지 않는다
183
+ (0, _vitest.expect)(container.querySelector(SELECTOR.ACTION_BUTTON)).toBeNull();
184
+ });
185
+ });
186
+ (0, _vitest.describe)('hidePermanentlyLabel (React) — Notification 공유 Props 경유', () => {
187
+ (0, _vitest.it)('R-11: type="message"에서 공유 Props의 라벨이 전달된다', () => {
188
+ // Given: 공유 Notification props로 라벨이 주어지고
189
+ // When: type="message"로 렌더하면
190
+ const container = renderNotification({
191
+ type: 'message',
192
+ onHidePermanently: _vitest.vi.fn(),
193
+ hidePermanentlyLabel: '공유 프롭 라벨'
194
+ });
195
+ // Then: message 구현까지 라벨이 도달한다
196
+ (0, _vitest.expect)(container.querySelector(SELECTOR.MESSAGE_HIDE_LINK)?.textContent).toBe('공유 프롭 라벨');
197
+ });
198
+ (0, _vitest.it)('R-12: type="full-width"에서 공유 Props의 라벨이 전달된다', () => {
199
+ // Given: 공유 Notification props로 라벨이 주어지고
200
+ // When: type="full-width"로 렌더하면
201
+ const container = renderNotification({
202
+ type: 'full-width',
203
+ onHidePermanently: _vitest.vi.fn(),
204
+ hidePermanentlyLabel: '공유 프롭 라벨'
205
+ });
206
+ // Then: full-width 구현까지 라벨이 도달한다
207
+ (0, _vitest.expect)(container.querySelector(SELECTOR.FULL_WIDTH_LINK)?.textContent).toBe('공유 프롭 라벨');
208
+ });
209
+ });
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PostcodeSearchModal = void 0;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _classnames = _interopRequireDefault(require("classnames"));
9
+ var _react = require("react");
10
+ var _button = require("../../action/button");
11
+ var _emptyState = require("../../feedback-and-status/empty-state");
12
+ var _inputBase = require("../../forms-and-input/input-base");
13
+ var _radio = require("../../forms-and-input/radio");
14
+ var _pagination = require("../../navigation/pagination");
15
+ var _modal = require("../modal");
16
+ var _PostcodeSearchModalTip = require("./PostcodeSearchModalTip");
17
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
+ /** 기획서 기준값 */
19
+ const PAGE_SIZE = 15;
20
+ /** 페이지 번호 그룹 크기 — 초과 시 Pagination 이 점프 버튼을 노출한다 */
21
+ const PAGER_WINDOW = 10;
22
+ /** 반응형 전환 기준 — assets/styles/base/_variable.scss 의 $breakpoint 와 동일 */
23
+ const MOBILE_BREAKPOINT = 768;
24
+ /** 본문 스크롤 컨테이너. Modal.Content 가 overflow-y:auto 를 갖는다. */
25
+ const SCROLL_CONTAINER_SELECTOR = '.ncua-modal__content';
26
+ const MODAL_TITLE = '주소 찾기';
27
+ const SEARCH_PLACEHOLDER = '도로명, 지번, 건물명 검색';
28
+ const SEARCH_BUTTON_LABEL = '검색';
29
+ const EMPTY_TITLE = '검색 결과가 없습니다.';
30
+ const EMPTY_DESCRIPTION = '검색어를 다시 확인해 주세요.';
31
+ const ROAD_BADGE_LABEL = '도로명';
32
+ const JIBUN_BADGE_LABEL = '지번';
33
+ // 라디오 그룹 이름. `ncua-` 로 시작하면 MCP 의 BEM 추출기가 CSS 클래스로 오인하므로 접두사를 뺀다.
34
+ const RESULT_RADIO_NAME = 'postcode-search-modal-result';
35
+ /** E0006·E0008 외 나머지 코드는 모두 같은 문구를 쓴다(기획서: E0009~E0013 통합). */
36
+ const DEFAULT_ERROR_HINT = '검색어를 다시 입력해주세요.';
37
+ const ERROR_HINT = {
38
+ E0006: '시도명으로는 검색이 불가합니다.',
39
+ E0008: '검색어를 두 글자 이상 입력해주세요.'
40
+ };
41
+ /** 선택 표시를 판정하는 키. 우편번호만으로는 같은 건물의 다른 지번을 구분하지 못한다. */
42
+ const resultKey = _ref => {
43
+ let {
44
+ zipCode,
45
+ roadAddress,
46
+ jibunAddress
47
+ } = _ref;
48
+ return `${zipCode}|${roadAddress}|${jibunAddress}`;
49
+ };
50
+ /** SSR(도큐사우루스 등)에서는 초기값을 false(=PC)로 두고 mount 이후 갱신한다. */
51
+ const useIsMobileViewport = () => {
52
+ const [isMobile, setIsMobile] = (0, _react.useState)(false);
53
+ (0, _react.useEffect)(() => {
54
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;
55
+ const mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`);
56
+ const sync = () => setIsMobile(mediaQuery.matches);
57
+ sync();
58
+ mediaQuery.addEventListener('change', sync);
59
+ return () => {
60
+ mediaQuery.removeEventListener('change', sync);
61
+ };
62
+ }, []);
63
+ return isMobile;
64
+ };
65
+ /**
66
+ * 본문이 위로 스크롤됐는지 여부.
67
+ * 고정된 검색 영역과 스크롤되는 결과 사이의 경계선을 스크롤 중에만 그리기 위해 쓴다.
68
+ * enabled 가 false 면 감시를 걸지 않고 항상 false 를 돌려준다.
69
+ */
70
+ const useIsContentScrolled = (rootRef, enabled) => {
71
+ const [isScrolled, setIsScrolled] = (0, _react.useState)(false);
72
+ (0, _react.useEffect)(() => {
73
+ if (!enabled) {
74
+ setIsScrolled(false);
75
+ return;
76
+ }
77
+ const scroller = rootRef.current?.closest(SCROLL_CONTAINER_SELECTOR);
78
+ if (!scroller) return;
79
+ const sync = () => setIsScrolled(scroller.scrollTop > 0);
80
+ // 결과가 바뀌어도 스크롤 위치는 남을 수 있어 구독 시점의 값으로 먼저 맞춘다.
81
+ sync();
82
+ scroller.addEventListener('scroll', sync, {
83
+ passive: true
84
+ });
85
+ return () => {
86
+ scroller.removeEventListener('scroll', sync);
87
+ };
88
+ }, [rootRef, enabled]);
89
+ return isScrolled;
90
+ };
91
+ const PostcodeSearchModal = _ref2 => {
92
+ let {
93
+ open,
94
+ onClose,
95
+ zIndex,
96
+ className,
97
+ searchQuery,
98
+ onSearchQueryChange,
99
+ onSearch,
100
+ results,
101
+ totalCount,
102
+ currentPage,
103
+ onPageChange,
104
+ pageSize = PAGE_SIZE,
105
+ loading = false,
106
+ errorCode,
107
+ onSelect
108
+ } = _ref2;
109
+ const isMobile = useIsMobileViewport();
110
+ const rootRef = (0, _react.useRef)(null);
111
+ // 선택 표시는 컴포넌트가 직접 들고 있는다. 사용처는 onSelect 로 값만 받으면 된다.
112
+ // 우편번호는 한 페이지에 중복될 수 있어 주소 전체를 키로 쓴다.
113
+ const [selectedKey, setSelectedKey] = (0, _react.useState)();
114
+ // 검색이 실행된 검색어. 입력 중에는 갱신되지 않는다.
115
+ // 초기값은 마운트 시점의 searchQuery — 사용처가 검색어를 채운 채로 열면 그 검색은 이미 실행된 것으로 본다.
116
+ const [searchedQuery, setSearchedQuery] = (0, _react.useState)(searchQuery);
117
+ // Modal 은 닫혀도 이 컴포넌트를 언마운트하지 않아 내부 상태가 다음 열기까지 남는다.
118
+ // 열리는 순간 이전 세션의 선택·검색 상태를 지운다.
119
+ // biome-ignore lint/correctness/useExhaustiveDependencies: searchQuery 를 deps 에 넣으면 입력할 때마다 초기화된다
120
+ (0, _react.useEffect)(() => {
121
+ if (!open) return;
122
+ setSelectedKey(undefined);
123
+ setSearchedQuery(searchQuery);
124
+ }, [open]);
125
+ const hasError = Boolean(errorCode);
126
+ const hasResults = !hasError && results.length > 0;
127
+ // 검색을 실행한 검색어와 현재 입력값이 같을 때만 "결과 없음"으로 본다.
128
+ // 입력 중에는 두 값이 달라지고, 응답을 기다리는 동안에는 loading 이 막는다.
129
+ const showEmptyState = !hasError && !loading && results.length === 0 && searchQuery !== '' && searchQuery === searchedQuery;
130
+ // 결과 목록이 있을 때만 스크롤이 생기므로 그때만 감시한다.
131
+ const isContentScrolled = useIsContentScrolled(rootRef, open && hasResults);
132
+ const handleSearchQueryChange = event => {
133
+ onSearchQueryChange(event.target.value);
134
+ };
135
+ const handleSearch = () => {
136
+ setSearchedQuery(searchQuery);
137
+ onSearch();
138
+ };
139
+ const handleSearchKeyDown = event => {
140
+ // 한글 조합 중의 Enter 는 글자 확정이므로 검색으로 보지 않는다.
141
+ if (event.key !== 'Enter' || event.nativeEvent.isComposing) return;
142
+ event.preventDefault();
143
+ handleSearch();
144
+ };
145
+ return (0, _jsxRuntime.jsxs)(_modal.Modal, {
146
+ isOpen: open,
147
+ onClose: onClose,
148
+ size: "md",
149
+ zIndex: zIndex,
150
+ className: className,
151
+ children: [(0, _jsxRuntime.jsx)(_modal.Modal.Header, {
152
+ title: MODAL_TITLE,
153
+ onClose: onClose,
154
+ showDivider: true
155
+ }), (0, _jsxRuntime.jsx)(_modal.Modal.Content, {
156
+ children: (0, _jsxRuntime.jsxs)("div", {
157
+ className: "ncua-postcode-search-modal",
158
+ ref: rootRef,
159
+ children: [(0, _jsxRuntime.jsxs)("div", {
160
+ className: (0, _classnames.default)('ncua-postcode-search-modal__search-area', {
161
+ 'is-fixed': hasResults && isContentScrolled
162
+ }),
163
+ children: [(0, _jsxRuntime.jsxs)("div", {
164
+ className: "ncua-postcode-search-modal__search",
165
+ children: [(0, _jsxRuntime.jsx)(_inputBase.InputBase, {
166
+ className: "ncua-postcode-search-modal__search-input",
167
+ size: "sm",
168
+ fullWidth: true,
169
+ value: searchQuery,
170
+ onChange: handleSearchQueryChange,
171
+ onKeyDown: handleSearchKeyDown,
172
+ placeholder: SEARCH_PLACEHOLDER,
173
+ destructive: hasError,
174
+ hintText: errorCode ? ERROR_HINT[errorCode] ?? DEFAULT_ERROR_HINT : undefined
175
+ }), (0, _jsxRuntime.jsx)(_button.Button, {
176
+ className: "ncua-postcode-search-modal__search-button",
177
+ label: SEARCH_BUTTON_LABEL,
178
+ size: "sm",
179
+ hierarchy: "secondary",
180
+ onClick: handleSearch
181
+ })]
182
+ }), hasResults && (0, _jsxRuntime.jsxs)("div", {
183
+ children: [(0, _jsxRuntime.jsx)("p", {
184
+ className: "ncua-postcode-search-modal__result-title",
185
+ children: _PostcodeSearchModalTip.TIP_TITLE
186
+ }), (0, _jsxRuntime.jsx)("p", {
187
+ className: "ncua-postcode-search-modal__result-desc",
188
+ children: "\uB3C4\uB85C\uBA85+\uAC74\uBB3C\uBC88\uD638, \uC9C0\uC5ED\uBA85+\uC9C0\uBC88, \uC9C0\uC5ED\uBA85+\uAC74\uBB3C\uBA85(\uC544\uD30C\uD2B8\uBA85)"
189
+ })]
190
+ })]
191
+ }), !hasResults && (0, _jsxRuntime.jsx)(PostcodeSearchModal.Tip, {}), hasResults && (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
192
+ children: [(0, _jsxRuntime.jsxs)("p", {
193
+ className: "ncua-postcode-search-modal__count",
194
+ children: ["\uAC80\uC0C9 ", (0, _jsxRuntime.jsx)("b", {
195
+ children: totalCount
196
+ }), "\uAC1C"]
197
+ }), (0, _jsxRuntime.jsx)("ul", {
198
+ className: "ncua-postcode-search-modal__list",
199
+ children: results.map((result, index) => (0, _jsxRuntime.jsx)("li", {
200
+ className: "ncua-postcode-search-modal__item",
201
+ children: (0, _jsxRuntime.jsx)(_radio.Radio, {
202
+ name: RESULT_RADIO_NAME,
203
+ value: result.zipCode,
204
+ checked: selectedKey === resultKey(result),
205
+ onChange: () => {
206
+ setSelectedKey(resultKey(result));
207
+ onSelect(result);
208
+ },
209
+ text: (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
210
+ children: [(0, _jsxRuntime.jsxs)("span", {
211
+ className: "ncua-postcode-search-modal__addr-line",
212
+ children: [(0, _jsxRuntime.jsx)("span", {
213
+ className: "ncua-postcode-search-modal__badge",
214
+ children: ROAD_BADGE_LABEL
215
+ }), (0, _jsxRuntime.jsx)("span", {
216
+ className: "ncua-postcode-search-modal__item-road",
217
+ children: result.roadAddress
218
+ }), (0, _jsxRuntime.jsx)("span", {
219
+ className: "ncua-postcode-search-modal__item-zip",
220
+ children: result.zipCode
221
+ })]
222
+ }), (0, _jsxRuntime.jsxs)("span", {
223
+ className: "ncua-postcode-search-modal__addr-line",
224
+ children: [(0, _jsxRuntime.jsx)("span", {
225
+ className: "ncua-postcode-search-modal__badge",
226
+ children: JIBUN_BADGE_LABEL
227
+ }), (0, _jsxRuntime.jsx)("span", {
228
+ className: "ncua-postcode-search-modal__item-jibun",
229
+ children: result.jibunAddress
230
+ })]
231
+ })]
232
+ })
233
+ })
234
+ }, `${result.zipCode}-${index}`))
235
+ }), (0, _jsxRuntime.jsx)("div", {
236
+ className: "ncua-postcode-search-modal__pagination",
237
+ children: (0, _jsxRuntime.jsx)(_pagination.Pagination, {
238
+ totalCount: totalCount,
239
+ itemCountPerPage: pageSize,
240
+ pageCount: PAGER_WINDOW,
241
+ currentPage: currentPage,
242
+ breakPoint: isMobile ? 'mo' : 'pc',
243
+ onPageChange: onPageChange
244
+ })
245
+ })]
246
+ }), showEmptyState && (0, _jsxRuntime.jsx)("div", {
247
+ className: "ncua-postcode-search-modal__empty",
248
+ children: (0, _jsxRuntime.jsx)(_emptyState.EmptyState, {
249
+ title: EMPTY_TITLE,
250
+ description: EMPTY_DESCRIPTION
251
+ })
252
+ })]
253
+ })
254
+ })]
255
+ });
256
+ };
257
+ exports.PostcodeSearchModal = PostcodeSearchModal;
258
+ PostcodeSearchModal.displayName = 'PostcodeSearchModal';
259
+ PostcodeSearchModal.Tip = _PostcodeSearchModalTip.Tip;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Tip = exports.TIP_TITLE = void 0;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _uiAdminIcon = require("@ncds/ui-admin-icon");
9
+ var _classnames = _interopRequireDefault(require("classnames"));
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ const TIP_LABEL = 'Tip';
12
+ /** Tip 카드 첫 줄 안내 문구 (기획서 TIPS.searchTitle) */
13
+ const TIP_TITLE = exports.TIP_TITLE = '아래 조합으로 검색하면 원하는 주소를 더 쉽게 찾을 수 있어요.';
14
+ /** Tip 에 노출할 검색어 조합과 예시 주소 (기획서 TIPS.searchTips) */
15
+ const TIP_ITEMS = [{
16
+ combination: '도로명 + 건물번호',
17
+ example: '디지털로26길 43'
18
+ }, {
19
+ combination: '지역명(동·리) + 번지',
20
+ example: '구로동 235-3'
21
+ }, {
22
+ combination: '지역명(동·리) + 건물명(아파트명)',
23
+ example: '구로동 대륭포스트타워8차'
24
+ }];
25
+ const TIP_EXAMPLE_PREFIX = '예)';
26
+ const TIP_ICON_SIZE = 16;
27
+ /**
28
+ * 검색어 조합을 안내하는 Tip 카드.
29
+ * 결과 목록이 없는 화면(초기·오류·결과 없음)에서만 노출된다.
30
+ * 결과가 있을 때는 모달이 안내 문구 한 줄만 검색 영역에 붙여 고정한다.
31
+ */
32
+ const Tip = _ref => {
33
+ let {
34
+ className
35
+ } = _ref;
36
+ return (0, _jsxRuntime.jsxs)("div", {
37
+ className: (0, _classnames.default)('ncua-postcode-search-modal__tip', className),
38
+ children: [(0, _jsxRuntime.jsxs)("p", {
39
+ className: "ncua-postcode-search-modal__tip-label",
40
+ children: [(0, _jsxRuntime.jsx)(_uiAdminIcon.AnnotationInfoFill, {
41
+ className: "ncua-postcode-search-modal__tip-icon",
42
+ width: TIP_ICON_SIZE,
43
+ height: TIP_ICON_SIZE
44
+ }), TIP_LABEL]
45
+ }), (0, _jsxRuntime.jsx)("p", {
46
+ className: "ncua-postcode-search-modal__tip-title",
47
+ children: TIP_TITLE
48
+ }), (0, _jsxRuntime.jsx)("ul", {
49
+ className: "ncua-postcode-search-modal__tip-list",
50
+ children: TIP_ITEMS.map(_ref2 => {
51
+ let {
52
+ combination,
53
+ example
54
+ } = _ref2;
55
+ return (0, _jsxRuntime.jsxs)("li", {
56
+ className: "ncua-postcode-search-modal__tip-item",
57
+ children: [(0, _jsxRuntime.jsx)("span", {
58
+ className: "ncua-postcode-search-modal__tip-combination",
59
+ children: combination
60
+ }), (0, _jsxRuntime.jsx)("span", {
61
+ className: "ncua-postcode-search-modal__tip-example",
62
+ children: `${TIP_EXAMPLE_PREFIX} ${example}`
63
+ })]
64
+ }, combination);
65
+ })
66
+ })]
67
+ });
68
+ };
69
+ exports.Tip = Tip;
70
+ Tip.displayName = 'PostcodeSearchModal.Tip';