@ncds/ui-admin 1.8.26-alpha.7 → 1.8.26-alpha.8
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/assets/scripts/notification/FloatingNotification.js +4 -1
- package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +5 -1
- package/dist/cjs/assets/scripts/notification/MessageNotification.js +7 -16
- package/dist/cjs/assets/scripts/notification/const/classNames.js +0 -2
- package/dist/cjs/assets/scripts/notification/const/icons.js +1 -11
- package/dist/cjs/assets/scripts/notification/const/index.js +0 -12
- package/dist/cjs/assets/scripts/notification/const/sizes.js +1 -6
- package/dist/cjs/assets/scripts/notification/messageTemplate.js +4 -2
- package/dist/cjs/assets/scripts/notification/utils.js +13 -5
- package/dist/cjs/src/components/overlays/notification/MessageNotification.js +4 -19
- package/dist/cjs/src/components/overlays/notification/MessageNotificationParts.js +7 -18
- package/dist/cjs/src/components/overlays/notification/Notification.js +6 -5
- package/dist/cjs/src/components/overlays/notification/__tests__/MessageNotification.test.js +7 -7
- package/dist/cjs/src/components/overlays/notification/__tests__/Notification.test.js +8 -0
- package/dist/cjs/src/components/overlays/notification/__tests__/supportTextLink.test.js +1 -7
- package/dist/cjs/src/generated/scoped-css.js +1 -1
- package/dist/cjs/src/utils/url/isSafeUrl.js +1 -1
- package/dist/esm/assets/scripts/notification/FloatingNotification.js +4 -1
- package/dist/esm/assets/scripts/notification/FullWidthNotification.js +5 -1
- package/dist/esm/assets/scripts/notification/MessageNotification.js +9 -18
- package/dist/esm/assets/scripts/notification/const/classNames.js +0 -2
- package/dist/esm/assets/scripts/notification/const/icons.js +0 -10
- package/dist/esm/assets/scripts/notification/const/index.js +2 -2
- package/dist/esm/assets/scripts/notification/const/sizes.js +0 -5
- package/dist/esm/assets/scripts/notification/messageTemplate.js +4 -2
- package/dist/esm/assets/scripts/notification/utils.js +11 -4
- package/dist/esm/src/components/overlays/notification/MessageNotification.js +5 -20
- package/dist/esm/src/components/overlays/notification/MessageNotificationParts.js +7 -18
- package/dist/esm/src/components/overlays/notification/Notification.js +6 -5
- package/dist/esm/src/components/overlays/notification/__tests__/MessageNotification.test.js +8 -8
- package/dist/esm/src/components/overlays/notification/__tests__/Notification.test.js +8 -0
- package/dist/esm/src/components/overlays/notification/__tests__/supportTextLink.test.js +1 -7
- package/dist/esm/src/generated/scoped-css.js +1 -1
- package/dist/esm/src/utils/url/isSafeUrl.js +1 -1
- package/dist/temp/assets/scripts/notification/FloatingNotification.js +1 -1
- package/dist/temp/assets/scripts/notification/FullWidthNotification.js +1 -1
- package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +1 -1
- package/dist/temp/assets/scripts/notification/MessageNotification.js +9 -15
- package/dist/temp/assets/scripts/notification/const/classNames.d.ts +0 -2
- package/dist/temp/assets/scripts/notification/const/classNames.js +0 -2
- package/dist/temp/assets/scripts/notification/const/icons.d.ts +0 -2
- package/dist/temp/assets/scripts/notification/const/icons.js +0 -10
- package/dist/temp/assets/scripts/notification/const/index.d.ts +3 -3
- package/dist/temp/assets/scripts/notification/const/index.js +2 -2
- package/dist/temp/assets/scripts/notification/const/sizes.d.ts +0 -4
- package/dist/temp/assets/scripts/notification/const/sizes.js +0 -5
- package/dist/temp/assets/scripts/notification/const/types.d.ts +7 -22
- package/dist/temp/assets/scripts/notification/messageTemplate.d.ts +0 -2
- package/dist/temp/assets/scripts/notification/messageTemplate.js +2 -2
- package/dist/temp/assets/scripts/notification/utils.d.ts +10 -1
- package/dist/temp/assets/scripts/notification/utils.js +6 -3
- package/dist/temp/src/components/overlays/notification/FloatingNotification.d.ts +3 -4
- package/dist/temp/src/components/overlays/notification/FullWidthNotification.d.ts +2 -4
- package/dist/temp/src/components/overlays/notification/MessageNotification.d.ts +2 -18
- package/dist/temp/src/components/overlays/notification/MessageNotification.js +4 -8
- package/dist/temp/src/components/overlays/notification/MessageNotificationParts.d.ts +4 -10
- package/dist/temp/src/components/overlays/notification/MessageNotificationParts.js +4 -7
- package/dist/temp/src/components/overlays/notification/Notification.d.ts +8 -28
- package/dist/temp/src/components/overlays/notification/Notification.js +4 -2
- package/dist/temp/src/components/overlays/notification/__tests__/MessageNotification.test.js +12 -9
- package/dist/temp/src/components/overlays/notification/__tests__/Notification.test.js +5 -0
- package/dist/temp/src/components/overlays/notification/__tests__/supportTextLink.test.js +0 -11
- package/dist/temp/src/generated/scoped-css.js +1 -1
- package/dist/temp/src/utils/url/isSafeUrl.d.ts +1 -1
- package/dist/temp/src/utils/url/isSafeUrl.js +1 -1
- package/dist/types/assets/scripts/notification/MessageNotification.d.ts +1 -1
- package/dist/types/assets/scripts/notification/const/classNames.d.ts +0 -2
- package/dist/types/assets/scripts/notification/const/icons.d.ts +0 -2
- package/dist/types/assets/scripts/notification/const/index.d.ts +3 -3
- package/dist/types/assets/scripts/notification/const/sizes.d.ts +0 -4
- package/dist/types/assets/scripts/notification/const/types.d.ts +7 -22
- package/dist/types/assets/scripts/notification/messageTemplate.d.ts +0 -2
- package/dist/types/assets/scripts/notification/utils.d.ts +10 -1
- package/dist/types/src/components/overlays/notification/FloatingNotification.d.ts +3 -4
- package/dist/types/src/components/overlays/notification/FullWidthNotification.d.ts +2 -4
- package/dist/types/src/components/overlays/notification/MessageNotification.d.ts +2 -18
- package/dist/types/src/components/overlays/notification/MessageNotificationParts.d.ts +4 -10
- package/dist/types/src/components/overlays/notification/Notification.d.ts +8 -28
- package/dist/types/src/utils/url/isSafeUrl.d.ts +1 -1
- package/dist/ui-admin/assets/styles/style.css +0 -19
- package/package.json +1 -1
|
@@ -19,7 +19,7 @@ function resolveBaseOrigin() {
|
|
|
19
19
|
* javascript:, data:, file: 등 위험 프로토콜로 인한 XSS / 외부 페이로드 로딩을 차단.
|
|
20
20
|
*
|
|
21
21
|
* URL 파서가 앞뒤 공백·탭·개행 제거와 scheme 소문자화를 하므로 ` JavaScript:` 같은 우회도 막힌다.
|
|
22
|
-
* React(`
|
|
22
|
+
* React(`FullWidthNotification`)와 Vanilla(`renderSupportingText`)가 공유한다.
|
|
23
23
|
*/
|
|
24
24
|
function isSafeUrl(url) {
|
|
25
25
|
if (!url.trim()) return false;
|
|
@@ -89,7 +89,10 @@ export class FloatingNotification {
|
|
|
89
89
|
<div class="${CLASS_NAMES.FLOATING.TITLE_WRAPPER}">
|
|
90
90
|
<span class="${CLASS_NAMES.FLOATING.TITLE}">${title}</span>
|
|
91
91
|
</div>
|
|
92
|
-
${renderSupportingText(
|
|
92
|
+
${renderSupportingText({
|
|
93
|
+
supportingText,
|
|
94
|
+
className: CLASS_NAMES.FLOATING.SUPPORTING_TEXT
|
|
95
|
+
})}
|
|
93
96
|
${actions && actions.length > 0 ? renderActions(actions, CLASS_NAMES.FLOATING.ACTIONS) : ''}
|
|
94
97
|
</div>
|
|
95
98
|
</div>
|
|
@@ -66,7 +66,11 @@ export class FullWidthNotification {
|
|
|
66
66
|
<div class="${CLASS_NAMES.FULL_WIDTH.ICON}">${iconHtml}</div>
|
|
67
67
|
<div class="${CLASS_NAMES.FULL_WIDTH.TEXT_CONTAINER}">
|
|
68
68
|
<span class="${CLASS_NAMES.FULL_WIDTH.TITLE}">${title}</span>
|
|
69
|
-
${renderSupportingText(
|
|
69
|
+
${renderSupportingText({
|
|
70
|
+
supportingText,
|
|
71
|
+
className: CLASS_NAMES.FULL_WIDTH.SUPPORTING_TEXT,
|
|
72
|
+
supportTextLink
|
|
73
|
+
})}
|
|
70
74
|
</div>
|
|
71
75
|
</div>
|
|
72
76
|
<div class="${CLASS_NAMES.FULL_WIDTH.ACTIONS_CONTAINER}">
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FeaturedIcon } from '../featuredIcon';
|
|
2
2
|
import { sanitizeHtml } from '../utils/sanitize';
|
|
3
|
-
import { CLASS_NAMES,
|
|
3
|
+
import { CLASS_NAMES, MESSAGE_ICON_MAP, MESSAGE_SIZES } from './const';
|
|
4
4
|
import { buildMessageTemplate } from './messageTemplate';
|
|
5
|
-
import {
|
|
5
|
+
import { bindNotificationEvents, createWrapperElement, setupAutoClose } from './utils';
|
|
6
6
|
export class MessageNotification {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
this.options = {
|
|
@@ -28,19 +28,13 @@ export class MessageNotification {
|
|
|
28
28
|
onClose,
|
|
29
29
|
hideCloseButton,
|
|
30
30
|
onHidePermanently,
|
|
31
|
-
hidePermanentlyLabel
|
|
32
|
-
variant,
|
|
33
|
-
supportTextLink,
|
|
34
|
-
supportTextLinkTarget
|
|
31
|
+
hidePermanentlyLabel
|
|
35
32
|
} = this.options;
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const wrapper = createWrapperElement(CLASS_NAMES.MESSAGE.BASE, color, wrapperClassName);
|
|
39
|
-
const featuredIconElement = this.createFeaturedIcon(color, isBlock);
|
|
33
|
+
const wrapper = createWrapperElement(CLASS_NAMES.MESSAGE.BASE, color, className);
|
|
34
|
+
const featuredIconElement = this.createFeaturedIcon(color);
|
|
40
35
|
wrapper.innerHTML = sanitizeHtml(buildMessageTemplate({
|
|
41
36
|
title,
|
|
42
37
|
supportingText,
|
|
43
|
-
supportTextLink: isBlock ? supportTextLink : undefined,
|
|
44
38
|
color,
|
|
45
39
|
actions,
|
|
46
40
|
onClose,
|
|
@@ -48,7 +42,6 @@ export class MessageNotification {
|
|
|
48
42
|
onHidePermanently,
|
|
49
43
|
hidePermanentlyLabel
|
|
50
44
|
}));
|
|
51
|
-
applySupportTextLinkTarget(wrapper, CLASS_NAMES.MESSAGE.TEXT_CONTAINER, CLASS_NAMES.MESSAGE.LINK, supportTextLinkTarget);
|
|
52
45
|
// FeaturedIcon을 content-wrapper에 추가
|
|
53
46
|
if (featuredIconElement) {
|
|
54
47
|
const contentWrapper = wrapper.querySelector(`.${CLASS_NAMES.MESSAGE.CONTENT_WRAPPER}`);
|
|
@@ -58,16 +51,14 @@ export class MessageNotification {
|
|
|
58
51
|
}
|
|
59
52
|
return wrapper;
|
|
60
53
|
}
|
|
61
|
-
/** 색상별 FeaturedIcon.
|
|
62
|
-
createFeaturedIcon(color
|
|
63
|
-
const iconFunction = (isBlock ? MESSAGE_BLOCK_ICON_MAP : MESSAGE_ICON_MAP)[color];
|
|
64
|
-
const sizes = isBlock ? MESSAGE_BLOCK_SIZES : MESSAGE_SIZES;
|
|
54
|
+
/** 색상별 FeaturedIcon. md(40px 원 / 20px 아이콘) 고정. */
|
|
55
|
+
createFeaturedIcon(color) {
|
|
65
56
|
this.featuredIcon = FeaturedIcon.create({
|
|
66
|
-
svgString:
|
|
57
|
+
svgString: MESSAGE_ICON_MAP[color](MESSAGE_SIZES.ICON_PIXEL),
|
|
67
58
|
theme: 'light-circle',
|
|
68
59
|
// FeaturedIcon 에는 info 색이 없다. info 의 violet 톤은 message SCSS 가 덮어쓴다.
|
|
69
60
|
color: color === 'info' ? 'neutral' : color,
|
|
70
|
-
size:
|
|
61
|
+
size: MESSAGE_SIZES.FEATURED_ICON,
|
|
71
62
|
// React 구현과 동일하게 `__icon`(flex-shrink: 0)을 부여한다.
|
|
72
63
|
// 누락 시 긴 타이틀에서 아이콘이 찌그러진다.
|
|
73
64
|
className: CLASS_NAMES.MESSAGE.ICON
|
|
@@ -27,7 +27,6 @@ export const CLASS_NAMES = {
|
|
|
27
27
|
},
|
|
28
28
|
MESSAGE: {
|
|
29
29
|
BASE: 'ncua-message-notification',
|
|
30
|
-
BLOCK: 'ncua-message-notification--block',
|
|
31
30
|
CONTAINER: 'ncua-message-notification__container',
|
|
32
31
|
CONTENT: 'ncua-message-notification__content',
|
|
33
32
|
CONTENT_WRAPPER: 'ncua-message-notification__content-wrapper',
|
|
@@ -35,7 +34,6 @@ export const CLASS_NAMES = {
|
|
|
35
34
|
TEXT_CONTAINER: 'ncua-message-notification__text-container',
|
|
36
35
|
TITLE: 'ncua-message-notification__title',
|
|
37
36
|
SUPPORTING_TEXT: 'ncua-message-notification__supporting-text',
|
|
38
|
-
LINK: 'ncua-message-notification__link',
|
|
39
37
|
ACTIONS_CONTAINER: 'ncua-message-notification__actions-container',
|
|
40
38
|
ACTIONS: 'ncua-message-notification__actions',
|
|
41
39
|
// 다시 보지 않기 + 닫기 그룹 전용 래퍼. ACTIONS_CONTAINER를 재사용하면
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* message block 변형의 InfoCircle. `SVG_ICONS['info-circle']`은 full-width 전용 class·color 가 박혀 있어
|
|
3
|
-
* 쓰지 않는다 (`.ncua-full-width-notification__icon`은 모바일에서 숨겨진다).
|
|
4
|
-
*/
|
|
5
|
-
const INFO_CIRCLE_ICON = size => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" fill="none" viewBox="0 0 24 24" stroke="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 16v-4m0-4h.01M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10"></path></svg>`;
|
|
6
1
|
// SVG 아이콘들 (크기는 동적으로 설정)
|
|
7
2
|
export const SVG_ICONS = {
|
|
8
3
|
'pin-02': size => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" fill="none" viewBox="0 0 24 24" stroke="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.377 15.616 2.72 21.273m8.974-14.631-1.56 1.56a2 2 0 0 1-.264.242 1 1 0 0 1-.207.111c-.082.032-.17.05-.347.085l-3.665.733c-.952.19-1.428.286-1.65.537a1 1 0 0 0-.243.8c.046.333.39.677 1.076 1.363l7.086 7.086c.686.687 1.03 1.03 1.362 1.076a1 1 0 0 0 .801-.242c.251-.223.346-.7.537-1.651l.733-3.665c.035-.176.053-.265.085-.347a1 1 0 0 1 .11-.207c.051-.072.115-.136.242-.263l1.561-1.561c.082-.082.122-.122.167-.158q.06-.047.126-.085c.05-.029.103-.051.208-.097l2.495-1.069c.727-.312 1.091-.467 1.256-.72a1 1 0 0 0 .144-.747c-.06-.295-.34-.575-.9-1.135l-5.142-5.143c-.56-.56-.84-.84-1.135-.9a1 1 0 0 0-.748.145c-.252.165-.407.529-.72 1.256l-1.068 2.495a2 2 0 0 1-.097.208 1 1 0 0 1-.085.126 2 2 0 0 1-.158.167"></path></svg>`,
|
|
@@ -35,9 +30,4 @@ export const MESSAGE_ICON_MAP = {
|
|
|
35
30
|
warning: SVG_ICONS['alert-circle'],
|
|
36
31
|
success: SVG_ICONS['check-circle'],
|
|
37
32
|
info: SVG_ICONS['pin-02']
|
|
38
|
-
};
|
|
39
|
-
/** block 변형 아이콘. info 만 InfoCircle 로 다르다 (#536 유지). */
|
|
40
|
-
export const MESSAGE_BLOCK_ICON_MAP = {
|
|
41
|
-
...MESSAGE_ICON_MAP,
|
|
42
|
-
info: INFO_CIRCLE_ICON
|
|
43
33
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// Re-export all constants and types from separate files
|
|
2
2
|
export { CLASS_NAMES } from './classNames';
|
|
3
|
-
export { FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP, ICON_MAP,
|
|
4
|
-
export { CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FEATURED_ICON_SIZES, FULL_WIDTH_SIZES, getSizes, ICON_PIXEL_SIZES,
|
|
3
|
+
export { FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP, ICON_MAP, MESSAGE_ICON_MAP, SVG_ICONS } from './icons';
|
|
4
|
+
export { CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FEATURED_ICON_SIZES, FULL_WIDTH_SIZES, getSizes, ICON_PIXEL_SIZES, MESSAGE_SIZES } from './sizes';
|
|
@@ -33,11 +33,6 @@ export const MESSAGE_SIZES = {
|
|
|
33
33
|
ICON_PIXEL: '20',
|
|
34
34
|
CLOSE_BUTTON: '20'
|
|
35
35
|
};
|
|
36
|
-
// Message 알림 Block 변형 사이즈. 'sm' = 32px 원 / 16px 아이콘. 닫기 버튼은 default 와 같다.
|
|
37
|
-
export const MESSAGE_BLOCK_SIZES = {
|
|
38
|
-
FEATURED_ICON: 'sm',
|
|
39
|
-
ICON_PIXEL: '16'
|
|
40
|
-
};
|
|
41
36
|
// 사이즈 유틸리티 함수들
|
|
42
37
|
export const getSizes = {
|
|
43
38
|
featuredIcon: isMobile => isMobile ? FEATURED_ICON_SIZES.MOBILE : FEATURED_ICON_SIZES.DESKTOP,
|
|
@@ -15,7 +15,6 @@ export function buildMessageTemplate(params) {
|
|
|
15
15
|
const {
|
|
16
16
|
title,
|
|
17
17
|
supportingText,
|
|
18
|
-
supportTextLink,
|
|
19
18
|
color,
|
|
20
19
|
actions,
|
|
21
20
|
onClose,
|
|
@@ -29,7 +28,10 @@ export function buildMessageTemplate(params) {
|
|
|
29
28
|
<div class="${CLASS_NAMES.MESSAGE.CONTENT_WRAPPER}">
|
|
30
29
|
<div class="${CLASS_NAMES.MESSAGE.TEXT_CONTAINER}">
|
|
31
30
|
<span class="${CLASS_NAMES.MESSAGE.TITLE}">${title}</span>
|
|
32
|
-
${renderSupportingText(
|
|
31
|
+
${renderSupportingText({
|
|
32
|
+
supportingText,
|
|
33
|
+
className: CLASS_NAMES.MESSAGE.SUPPORTING_TEXT
|
|
34
|
+
})}
|
|
33
35
|
</div>
|
|
34
36
|
</div>
|
|
35
37
|
${renderActionsContainer({
|
|
@@ -22,19 +22,26 @@ export function buildClassName(baseClass, color, className) {
|
|
|
22
22
|
function escapeHtmlAttr(str) {
|
|
23
23
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''');
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* 보조 텍스트 HTML을 만든다. `supportTextLink` 링크는 full-width 전용 클래스(`CLASS_NAMES.FULL_WIDTH.LINK`)로 렌더한다.
|
|
27
|
+
*/
|
|
28
|
+
export const renderSupportingText = _ref => {
|
|
29
|
+
let {
|
|
30
|
+
supportingText,
|
|
31
|
+
className,
|
|
32
|
+
supportTextLink
|
|
33
|
+
} = _ref;
|
|
27
34
|
if (!supportingText) return '';
|
|
28
35
|
// 본문(supportingText) 자체는 이 함수의 호출 결과가 wrapper.innerHTML에
|
|
29
36
|
// 들어가기 전에 DOMPurify로 sanitize되므로 별도 이스케이프하지 않는다.
|
|
30
37
|
// 이렇게 해야 <br>, <strong> 등 서식용 태그가 그대로 동작한다.
|
|
31
38
|
if (supportTextLink && isSafeUrl(supportTextLink)) {
|
|
32
39
|
const safeLink = escapeHtmlAttr(supportTextLink);
|
|
33
|
-
return `<a href="${safeLink}" class="${
|
|
40
|
+
return `<a href="${safeLink}" class="${CLASS_NAMES.FULL_WIDTH.LINK}"><span class="${className}">${supportingText}</span></a>`;
|
|
34
41
|
}
|
|
35
42
|
// unsafe URL이거나 link가 없으면 링크 없이 텍스트만 렌더링
|
|
36
43
|
return `<span class="${className}">${supportingText}</span>`;
|
|
37
|
-
}
|
|
44
|
+
};
|
|
38
45
|
/**
|
|
39
46
|
* `supportTextLinkTarget`이 `'_blank'`이면 `renderSupportingText`가 만든 링크를 새 탭으로 연다.
|
|
40
47
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { AlertCircle, AlertTriangle, CheckCircle,
|
|
2
|
+
import { AlertCircle, AlertTriangle, CheckCircle, Pin02 } from '@ncds/ui-admin-icon';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { forwardRef } from 'react';
|
|
5
5
|
import { MESSAGE_CLOSE_ICON_COLOR } from '../../../../constant/notification';
|
|
@@ -10,14 +10,9 @@ const DEFAULT_ICON_MAP = {
|
|
|
10
10
|
error: AlertTriangle,
|
|
11
11
|
warning: AlertCircle,
|
|
12
12
|
success: CheckCircle,
|
|
13
|
-
// 디자인 기준
|
|
13
|
+
// 디자인 기준 info 는 neutral 과 같은 핀이다.
|
|
14
14
|
info: Pin02
|
|
15
15
|
};
|
|
16
|
-
// block 의 info 는 InfoCircle 을 유지한다 (#536).
|
|
17
|
-
const BLOCK_ICON_MAP = {
|
|
18
|
-
...DEFAULT_ICON_MAP,
|
|
19
|
-
info: InfoCircle
|
|
20
|
-
};
|
|
21
16
|
const MessageNotification = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
22
17
|
let {
|
|
23
18
|
title,
|
|
@@ -31,13 +26,8 @@ const MessageNotification = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
31
26
|
actions,
|
|
32
27
|
onHidePermanently,
|
|
33
28
|
hidePermanentlyLabel,
|
|
34
|
-
variant = 'default',
|
|
35
|
-
supportTextLink,
|
|
36
|
-
supportTextLinkTarget,
|
|
37
29
|
...rest
|
|
38
30
|
} = _ref;
|
|
39
|
-
const isBlock = variant === 'block';
|
|
40
|
-
const iconMap = isBlock ? BLOCK_ICON_MAP : DEFAULT_ICON_MAP;
|
|
41
31
|
// FeaturedIcon 에는 info 색이 없다. info 의 violet 톤은 message SCSS 가 덮어쓴다.
|
|
42
32
|
const featuredIconColor = color === 'info' ? 'neutral' : color;
|
|
43
33
|
// message 는 부모가 언마운트로 닫으므로 hideCloseButton 기본 autoClose 를 적용하지 않는다.
|
|
@@ -50,9 +40,7 @@ const MessageNotification = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
50
40
|
}
|
|
51
41
|
return _jsx("div", {
|
|
52
42
|
ref: ref,
|
|
53
|
-
className: classNames('ncua-message-notification', `ncua-message-notification--${color}`,
|
|
54
|
-
'ncua-message-notification--block': isBlock
|
|
55
|
-
}, className),
|
|
43
|
+
className: classNames('ncua-message-notification', `ncua-message-notification--${color}`, className),
|
|
56
44
|
role: "alert",
|
|
57
45
|
...rest,
|
|
58
46
|
children: _jsx("div", {
|
|
@@ -62,11 +50,8 @@ const MessageNotification = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
62
50
|
children: [_jsx(MessageNotificationContent, {
|
|
63
51
|
title: title,
|
|
64
52
|
supportingText: supportingText,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
icon: Icon || iconMap[color],
|
|
68
|
-
iconColor: featuredIconColor,
|
|
69
|
-
iconSize: isBlock ? 'sm' : 'md'
|
|
53
|
+
icon: Icon || DEFAULT_ICON_MAP[color],
|
|
54
|
+
iconColor: featuredIconColor
|
|
70
55
|
}), _jsx(MessageNotificationActions, {
|
|
71
56
|
actions: actions,
|
|
72
57
|
onClose: onClose,
|
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Pin02, XClose } from '@ncds/ui-admin-icon';
|
|
3
3
|
import { HIDE_PERMANENTLY_LABEL } from '../../../../constant/notification';
|
|
4
|
-
import { isSafeUrl } from '../../../utils/url';
|
|
5
4
|
import { FeaturedIcon } from '../../image-and-icons/featured-icon/FeaturedIcon';
|
|
6
|
-
import { getLinkTargetAttributes } from './linkTarget';
|
|
7
5
|
import { NotificationActionButtons } from './NotificationActionButtons';
|
|
8
|
-
/** 아이콘 + 제목/서포팅 텍스트(
|
|
6
|
+
/** 아이콘 + 제목/서포팅 텍스트 영역. FeaturedIcon 은 md(40px 원 / 20px 아이콘) 고정. */
|
|
9
7
|
const MessageNotificationContent = _ref => {
|
|
10
8
|
let {
|
|
11
9
|
title,
|
|
12
10
|
supportingText,
|
|
13
|
-
supportTextLink,
|
|
14
|
-
supportTextLinkTarget,
|
|
15
11
|
icon,
|
|
16
|
-
iconColor
|
|
17
|
-
iconSize
|
|
12
|
+
iconColor
|
|
18
13
|
} = _ref;
|
|
19
14
|
const featuredIconProps = {
|
|
20
15
|
icon: icon || Pin02,
|
|
21
|
-
size:
|
|
16
|
+
size: 'md',
|
|
22
17
|
color: iconColor,
|
|
23
18
|
theme: 'light-circle'
|
|
24
19
|
};
|
|
25
|
-
const supportingTextNode = supportingText && _jsx("span", {
|
|
26
|
-
className: "ncua-message-notification__supporting-text",
|
|
27
|
-
children: supportingText
|
|
28
|
-
});
|
|
29
20
|
return _jsxs("div", {
|
|
30
21
|
className: "ncua-message-notification__content-wrapper",
|
|
31
22
|
children: [_jsx(FeaturedIcon, {
|
|
@@ -36,12 +27,10 @@ const MessageNotificationContent = _ref => {
|
|
|
36
27
|
children: [_jsx("span", {
|
|
37
28
|
className: "ncua-message-notification__title",
|
|
38
29
|
children: title
|
|
39
|
-
}),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
children: supportingTextNode
|
|
44
|
-
}) : supportingTextNode]
|
|
30
|
+
}), supportingText && _jsx("span", {
|
|
31
|
+
className: "ncua-message-notification__supporting-text",
|
|
32
|
+
children: supportingText
|
|
33
|
+
})]
|
|
45
34
|
})]
|
|
46
35
|
});
|
|
47
36
|
};
|
|
@@ -14,9 +14,13 @@ const Notification = /*#__PURE__*/forwardRef(
|
|
|
14
14
|
width,
|
|
15
15
|
supportTextLink,
|
|
16
16
|
supportTextLinkTarget,
|
|
17
|
-
|
|
18
|
-
...rest
|
|
17
|
+
...props
|
|
19
18
|
} = _ref;
|
|
19
|
+
// #580 에서 제거된 message `variant` 가 런타임에 넘어와도 DOM 속성으로 새지 않게 어떤 type 에도 전달하지 않는다.
|
|
20
|
+
const {
|
|
21
|
+
variant: _variant,
|
|
22
|
+
...rest
|
|
23
|
+
} = props;
|
|
20
24
|
if (type === 'floating') {
|
|
21
25
|
return _jsx(FloatingNotification, {
|
|
22
26
|
color: color,
|
|
@@ -36,9 +40,6 @@ const Notification = /*#__PURE__*/forwardRef(
|
|
|
36
40
|
if (type === 'message') {
|
|
37
41
|
return _jsx(MessageNotification, {
|
|
38
42
|
color: color,
|
|
39
|
-
variant: variant,
|
|
40
|
-
supportTextLink: supportTextLink,
|
|
41
|
-
supportTextLinkTarget: supportTextLinkTarget,
|
|
42
43
|
...rest,
|
|
43
44
|
ref: ref
|
|
44
45
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
|
-
import {
|
|
2
|
+
import { Pin02 } from '@ncds/ui-admin-icon';
|
|
3
3
|
import { createElement } from 'react';
|
|
4
4
|
import { createRoot } from 'react-dom/client';
|
|
5
5
|
import { act } from 'react-dom/test-utils';
|
|
@@ -156,7 +156,7 @@ describe('MessageNotification (React) — DOM 구조 계약', () => {
|
|
|
156
156
|
});
|
|
157
157
|
describe('MessageNotification (React) — color="info" (#568)', () => {
|
|
158
158
|
it('R-01: color="info" 는 neutral 로 폴백하지 않고 --info 수식자로 렌더한다', () => {
|
|
159
|
-
// Given/When:
|
|
159
|
+
// Given/When: info 색상으로 렌더하면
|
|
160
160
|
const root = renderMessageNotification({
|
|
161
161
|
color: 'info'
|
|
162
162
|
}).querySelector(SELECTOR.ROOT);
|
|
@@ -165,7 +165,7 @@ describe('MessageNotification (React) — color="info" (#568)', () => {
|
|
|
165
165
|
expect(root?.classList.contains('ncua-message-notification--neutral')).toBe(false);
|
|
166
166
|
});
|
|
167
167
|
it('R-02: 기본 info 아이콘은 neutral 과 같은 Pin02 다', () => {
|
|
168
|
-
// Given/When:
|
|
168
|
+
// Given/When: info 색상으로 렌더하면
|
|
169
169
|
const container = renderMessageNotification({
|
|
170
170
|
color: 'info'
|
|
171
171
|
});
|
|
@@ -175,7 +175,7 @@ describe('MessageNotification (React) — color="info" (#568)', () => {
|
|
|
175
175
|
expect(renderedIconPathD(container)).toBe(expected);
|
|
176
176
|
});
|
|
177
177
|
it('R-03: info 의 FeaturedIcon 은 neutral·md 로 렌더한다 (violet 톤은 SCSS 가 덮는다)', () => {
|
|
178
|
-
// Given/When:
|
|
178
|
+
// Given/When: info 색상으로 렌더하면
|
|
179
179
|
const icon = renderMessageNotification({
|
|
180
180
|
color: 'info'
|
|
181
181
|
}).querySelector(SELECTOR.ICON);
|
|
@@ -183,14 +183,14 @@ describe('MessageNotification (React) — color="info" (#568)', () => {
|
|
|
183
183
|
expect(icon?.classList.contains('ncua-featured-icon--neutral')).toBe(true);
|
|
184
184
|
expect(icon?.classList.contains('ncua-featured-icon--md')).toBe(true);
|
|
185
185
|
});
|
|
186
|
-
it('R-04: block
|
|
187
|
-
// Given/When:
|
|
186
|
+
it('R-04: 런타임에 variant="block" 이 넘어와도 info 아이콘은 Pin02 다 (#580 Block 변형 제거)', () => {
|
|
187
|
+
// Given/When: 제거된 variant prop 을 런타임 입력으로 넘겨 info 색상으로 렌더하면
|
|
188
188
|
const container = renderMessageNotification({
|
|
189
189
|
variant: 'block',
|
|
190
190
|
color: 'info'
|
|
191
191
|
});
|
|
192
|
-
// Then: 아이콘 path 가
|
|
193
|
-
const expected = iconPathD(
|
|
192
|
+
// Then: 아이콘 path 가 Pin02 와 같다
|
|
193
|
+
const expected = iconPathD(Pin02);
|
|
194
194
|
expect(expected).not.toBeNull();
|
|
195
195
|
expect(renderedIconPathD(container)).toBe(expected);
|
|
196
196
|
});
|
|
@@ -72,6 +72,14 @@ describe('Notification — type 별 위임', () => {
|
|
|
72
72
|
});
|
|
73
73
|
expect(element?.hasAttribute('supporttextlink')).toBe(false);
|
|
74
74
|
});
|
|
75
|
+
it.each(['floating', 'full-width', 'message'])('type=%s 에 제거된 variant="block" 을 주어도 DOM 속성으로 새지 않는다 (#580)', type => {
|
|
76
|
+
const element = render({
|
|
77
|
+
type,
|
|
78
|
+
variant: 'block'
|
|
79
|
+
});
|
|
80
|
+
expect(element).not.toBeNull();
|
|
81
|
+
expect(element?.hasAttribute('variant')).toBe(false);
|
|
82
|
+
});
|
|
75
83
|
it('정의되지 않은 type 은 아무것도 렌더하지 않는다', () => {
|
|
76
84
|
const element = render({
|
|
77
85
|
type: 'banner'
|
|
@@ -4,7 +4,6 @@ import { createRoot } from 'react-dom/client';
|
|
|
4
4
|
import { act } from 'react-dom/test-utils';
|
|
5
5
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
6
6
|
import { FullWidthNotification } from '../FullWidthNotification';
|
|
7
|
-
import { MessageNotification } from '../MessageNotification';
|
|
8
7
|
/**
|
|
9
8
|
* React 텍스트 링크(`supportTextLink`)의 프로토콜 검사 (#538 리뷰 H2).
|
|
10
9
|
* Vanilla `renderSupportingText` 와 같은 정책 — unsafe URL 이면 링크 없이 텍스트만 렌더한다.
|
|
@@ -27,12 +26,7 @@ function mount(element) {
|
|
|
27
26
|
});
|
|
28
27
|
return container;
|
|
29
28
|
}
|
|
30
|
-
const IMPLEMENTATIONS = [['
|
|
31
|
-
title: '공지',
|
|
32
|
-
variant: 'block',
|
|
33
|
-
supportingText: '자세히 보기',
|
|
34
|
-
supportTextLink
|
|
35
|
-
})), '.ncua-message-notification__supporting-text'], ['FullWidthNotification', supportTextLink => mount(/*#__PURE__*/createElement(FullWidthNotification, {
|
|
29
|
+
const IMPLEMENTATIONS = [['FullWidthNotification', supportTextLink => mount(/*#__PURE__*/createElement(FullWidthNotification, {
|
|
36
30
|
title: '공지',
|
|
37
31
|
supportingText: '자세히 보기',
|
|
38
32
|
supportTextLink
|