@planningcenter/sweetest-alert 0.1.0 → 0.2.3
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/package.json +25 -8
- package/.editorconfig +0 -8
- package/.github/CODEOWNERS +0 -1
- package/.github/workflows/pco-release-create-pr.yml +0 -24
- package/.github/workflows/pco-release-create-release-on-merge.yml +0 -17
- package/.github/workflows/pco-release-dependabot-automation.yml +0 -15
- package/.github/workflows/pco-release-require-changelog-update.yml +0 -22
- package/.github/workflows/pco-release-sync-release-by-label.yml +0 -18
- package/.stylelintrc.json +0 -8
- package/CHANGELOG.md +0 -6
- package/docs/demo.css +0 -198
- package/docs/demo.tsx +0 -306
- package/docs/index.html +0 -13
- package/eslint.config.js +0 -56
- package/src/index.ts +0 -1
- package/src/sweetest_alert.css +0 -62
- package/src/sweetest_alert.tsx +0 -130
- package/tests/sweetest_alert.test.jsx +0 -254
- package/tests/test-setup.ts +0 -13
- package/tsconfig.json +0 -23
- package/vite.config.ts +0 -11
- package/vitest.config.ts +0 -11
package/package.json
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/sweetest-alert",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "The sweetest alert ever",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "yarn@1.22.22",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./style.css": "./dist/style.css"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
7
22
|
"repository": {
|
|
8
23
|
"type": "git",
|
|
9
24
|
"url": "git+https://github.com/planningcenter/sweetest-alert.git"
|
|
@@ -14,24 +29,26 @@
|
|
|
14
29
|
"url": "https://github.com/planningcenter/sweetest-alert/issues"
|
|
15
30
|
},
|
|
16
31
|
"scripts": {
|
|
17
|
-
"dev": "vite",
|
|
18
|
-
"build": "vite build",
|
|
19
|
-
"
|
|
32
|
+
"dev": "vite --config vite.config.docs.ts",
|
|
33
|
+
"build": "vite build --config vite.config.docs.ts",
|
|
34
|
+
"build:lib": "vite build --config vite.config.lib.ts && tsc --project tsconfig.lib.json && cp src/sweetest_alert.css dist/style.css",
|
|
35
|
+
"build:docs": "vite build --config vite.config.docs.ts",
|
|
36
|
+
"preview": "vite preview --config vite.config.docs.ts",
|
|
20
37
|
"test": "vitest",
|
|
21
38
|
"lint:js": "eslint .",
|
|
22
39
|
"lint:css": "stylelint \"**/*.css\""
|
|
23
40
|
},
|
|
24
41
|
"peerDependencies": {
|
|
25
|
-
"@planningcenter/icons": "^15.
|
|
26
|
-
"@planningcenter/tapestry": "^2.
|
|
42
|
+
"@planningcenter/icons": "^15.26.0",
|
|
43
|
+
"@planningcenter/tapestry": "^2.5.0",
|
|
27
44
|
"react": "^18.3.1 <19",
|
|
28
45
|
"react-dom": "^18.3.1"
|
|
29
46
|
},
|
|
30
47
|
"devDependencies": {
|
|
31
48
|
"@eslint/js": "^9.37.0",
|
|
32
|
-
"@planningcenter/icons": "^15.
|
|
49
|
+
"@planningcenter/icons": "^15.26.0",
|
|
33
50
|
"@planningcenter/stylelint-config-pco": "^0.0.5",
|
|
34
|
-
"@planningcenter/tapestry": "^2.
|
|
51
|
+
"@planningcenter/tapestry": "^2.5.0",
|
|
35
52
|
"@testing-library/dom": "^10.4.1",
|
|
36
53
|
"@testing-library/jest-dom": "^6.9.1",
|
|
37
54
|
"@testing-library/react": "^16.3.0",
|
package/.editorconfig
DELETED
package/.github/CODEOWNERS
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
* @starzonmyarmz
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: PCO-Release - Release Automation
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
pull-requests: write
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
release-automation:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
- uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: "20"
|
|
20
|
-
cache: "yarn"
|
|
21
|
-
- uses: planningcenter/pco-release-action/release-by-pr@v1
|
|
22
|
-
with:
|
|
23
|
-
app-id: ${{ secrets.PCO_DEPENDENCIES_APP_ID }}
|
|
24
|
-
private-key: ${{ secrets.PCO_DEPENDENCIES_PRIVATE_KEY}}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
name: PCO-Release - Create Release on Merge
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
types: [closed]
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
create-release:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'pco-release-pending')
|
|
13
|
-
steps:
|
|
14
|
-
- uses: planningcenter/pco-release-action/create-release-on-merge@v1
|
|
15
|
-
with:
|
|
16
|
-
app-id: ${{ secrets.PCO_DEPENDENCIES_APP_ID }}
|
|
17
|
-
private-key: ${{ secrets.PCO_DEPENDENCIES_PRIVATE_KEY}}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
name: PCO-Release - Dependabot Automation (Update changelog)
|
|
2
|
-
|
|
3
|
-
on: pull_request
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
update-dependabot-pr-changelog:
|
|
7
|
-
if: github.actor == 'dependabot[bot]'
|
|
8
|
-
runs-on: ubuntu-latest
|
|
9
|
-
permissions:
|
|
10
|
-
contents: write
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
with:
|
|
14
|
-
ref: ${{ github.event.pull_request.head.ref }}
|
|
15
|
-
- uses: planningcenter/pco-release-action/dependabot-automation@v1
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: PCO-Release - Require Changelog Update
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
require-changelog-update:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
permissions:
|
|
11
|
-
pull-requests: read
|
|
12
|
-
contents: read
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- id: changed-files
|
|
16
|
-
uses: tj-actions/changed-files@v44
|
|
17
|
-
with:
|
|
18
|
-
files: CHANGELOG.md
|
|
19
|
-
- if: steps.changed-files.outputs.any_changed == 'false'
|
|
20
|
-
run: |
|
|
21
|
-
echo "Pull Requests require an update to the CHANGELOG.md file."
|
|
22
|
-
exit 1
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
on:
|
|
2
|
-
pull_request:
|
|
3
|
-
types: [labeled]
|
|
4
|
-
|
|
5
|
-
permissions:
|
|
6
|
-
contents: write
|
|
7
|
-
pull-requests: write
|
|
8
|
-
|
|
9
|
-
name: PCO-Release - Sync With Labels
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
sync-with-labels:
|
|
13
|
-
if: ${{ github.event.pull_request.head.ref == 'pco-release--internal' && (github.event.label.name == 'pco-release-patch' || github.event.label.name == 'pco-release-minor' || github.event.label.name == 'pco-release-major') }}
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: planningcenter/pco-release-action/sync-with-labels@v1
|
|
17
|
-
with:
|
|
18
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/.stylelintrc.json
DELETED
package/CHANGELOG.md
DELETED
package/docs/demo.css
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
* {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
margin: 0;
|
|
4
|
-
padding: 0;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
#root {
|
|
8
|
-
display: contents;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
body {
|
|
12
|
-
background: linear-gradient(135deg, var(--t-fill-color-tag-blue-040) 0%, var(--t-fill-color-tag-purple-030) 100%);
|
|
13
|
-
color: var(--t-text-color-default-primary);
|
|
14
|
-
display: grid;
|
|
15
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
16
|
-
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
17
|
-
sans-serif;
|
|
18
|
-
line-height: 1.4;
|
|
19
|
-
min-height: 100vh;
|
|
20
|
-
padding: var(--t-spacing-3);
|
|
21
|
-
place-items: center;
|
|
22
|
-
text-wrap: pretty;
|
|
23
|
-
gap:32px;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.container {
|
|
27
|
-
background: var(--t-surface-color-card);
|
|
28
|
-
border-radius: var(--t-border-radius-xl);
|
|
29
|
-
box-shadow: 0 var(--t-spacing-3) var(--t-spacing-6) var(--t-fill-color-transparency-dark-030);
|
|
30
|
-
max-width: 960px;
|
|
31
|
-
padding: var(--t-spacing-6);
|
|
32
|
-
width: 100%;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
h1 {
|
|
36
|
-
color: var(--t-text-color-default-headline);
|
|
37
|
-
font-size: 2.5rem;
|
|
38
|
-
margin: 0;
|
|
39
|
-
text-align: center;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
h2 {
|
|
43
|
-
color: var(--t-text-color-default-secondary);
|
|
44
|
-
font-size: 1.2rem;
|
|
45
|
-
font-weight: var(--t-font-weight-normal);
|
|
46
|
-
margin-bottom: var(--t-spacing-5);
|
|
47
|
-
text-align: center;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
ul {
|
|
51
|
-
margin: 1rem;
|
|
52
|
-
padding-left: 2ch;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
table {
|
|
56
|
-
border-collapse: collapse;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
th, td {
|
|
60
|
-
border-bottom: var(--t-border-width-default) solid var(--t-border-color-default-base);
|
|
61
|
-
text-align: left;
|
|
62
|
-
padding: var(--t-spacing-1);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
tr:last-child :is(th, td) {
|
|
66
|
-
border: 0;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.button-grid {
|
|
70
|
-
display: grid;
|
|
71
|
-
gap: var(--t-spacing-2);
|
|
72
|
-
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
73
|
-
margin-bottom: var(--t-spacing-4);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.demo-button {
|
|
77
|
-
background: var(--t-surface-color-card);
|
|
78
|
-
border: var(--t-border-width-thick) solid var(--t-border-color-default-base);
|
|
79
|
-
border-radius: 12px;
|
|
80
|
-
color: var(--t-text-color-default-secondary);
|
|
81
|
-
cursor: pointer;
|
|
82
|
-
display: flex;
|
|
83
|
-
flex-direction: column;
|
|
84
|
-
font-family: inherit;
|
|
85
|
-
font-size: 1rem;
|
|
86
|
-
gap: var(--t-spacing-half);
|
|
87
|
-
padding: var(--t-spacing-3);
|
|
88
|
-
text-align: left;
|
|
89
|
-
transition: all .2s ease;
|
|
90
|
-
|
|
91
|
-
&:hover {
|
|
92
|
-
box-shadow: 0 var(--t-spacing-half) var(--t-spacing-1) var(--t-fill-color-transparency-dark-010);
|
|
93
|
-
translate: 0 -2px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
&:active {
|
|
97
|
-
translate: 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
h3 {
|
|
101
|
-
color: var(--t-text-color-default-headline);
|
|
102
|
-
font-size: 1.1rem;
|
|
103
|
-
font-weight: var(--t-font-weight-semibold);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.info {
|
|
108
|
-
border-color: var(--t-border-color-status-info);
|
|
109
|
-
|
|
110
|
-
&:hover {
|
|
111
|
-
background: var(--t-fill-color-status-info-dim);
|
|
112
|
-
border-color: var(--t-border-color-status-info);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.success {
|
|
117
|
-
border-color: var(--t-border-color-status-success);
|
|
118
|
-
|
|
119
|
-
&:hover {
|
|
120
|
-
background: var(--t-fill-color-status-success-ghost);
|
|
121
|
-
border-color: var(--t-border-color-status-success);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.warning {
|
|
126
|
-
border-color: var(--t-border-color-status-warning);
|
|
127
|
-
|
|
128
|
-
&:hover {
|
|
129
|
-
background: var(--t-fill-color-status-warning-ghost);
|
|
130
|
-
border-color: var(--t-border-color-status-warning);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.error, .danger {
|
|
135
|
-
border-color: var(--t-border-color-status-error);
|
|
136
|
-
|
|
137
|
-
&:hover {
|
|
138
|
-
background: var(--t-fill-color-status-error-ghost);
|
|
139
|
-
border-color: var(--t-border-color-status-error);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.custom {
|
|
144
|
-
border-color: var(--t-border-color-default-darker);
|
|
145
|
-
|
|
146
|
-
&:hover {
|
|
147
|
-
background: var(--t-fill-color-tag-purple-010);
|
|
148
|
-
border-color: var(--t-border-color-default-darkest);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.tip {
|
|
153
|
-
background: var(--t-fill-color-neutral-090);
|
|
154
|
-
border: var(--t-border-width-default) solid var(--t-border-color-default-base);
|
|
155
|
-
border-radius: var(--t-border-radius-lg);
|
|
156
|
-
color: var(--t-text-color-default-secondary);
|
|
157
|
-
font-size: 1rem;
|
|
158
|
-
padding: var(--t-spacing-2) var(--t-spacing-3);
|
|
159
|
-
|
|
160
|
-
strong {
|
|
161
|
-
color: var(--t-text-color-default-primary);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.codeblock {
|
|
166
|
-
background: #2d2d2d;
|
|
167
|
-
border-radius: var(--t-border-radius-lg);
|
|
168
|
-
margin: var(--t-spacing-3) 0 var(--t-spacing-6);
|
|
169
|
-
overflow-x: auto;
|
|
170
|
-
padding: 0;
|
|
171
|
-
|
|
172
|
-
pre {
|
|
173
|
-
margin: 0;
|
|
174
|
-
padding: 0;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
code {
|
|
178
|
-
display: block;
|
|
179
|
-
font-family: 'Fira Code', 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
180
|
-
font-size: 0.9rem;
|
|
181
|
-
line-height: 1.5;
|
|
182
|
-
padding: var(--t-spacing-3);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@media (max-width: 640px) {
|
|
187
|
-
.demo-container {
|
|
188
|
-
padding: var(--t-spacing-4) var(--t-spacing-3);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
h1 {
|
|
192
|
-
font-size: 2rem;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.button-grid {
|
|
196
|
-
grid-template-columns: 1fr;
|
|
197
|
-
}
|
|
198
|
-
}
|
package/docs/demo.tsx
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
|
|
3
|
-
import { createRoot } from "react-dom/client"
|
|
4
|
-
import { useEffect, useRef } from "react"
|
|
5
|
-
import { SweetestAlert } from "../src/index"
|
|
6
|
-
import Prism from "prismjs"
|
|
7
|
-
import "prismjs/themes/prism-tomorrow.css"
|
|
8
|
-
import "prismjs/components/prism-jsx"
|
|
9
|
-
import "prismjs/components/prism-tsx"
|
|
10
|
-
import "prismjs/components/prism-bash"
|
|
11
|
-
|
|
12
|
-
import "../node_modules/@planningcenter/tapestry/dist/index.css"
|
|
13
|
-
import "../src/sweetest_alert.css"
|
|
14
|
-
import "./demo.css"
|
|
15
|
-
|
|
16
|
-
function CodeBlock({ code, language = "jsx" }: { code: string; language?: string }) {
|
|
17
|
-
const codeRef = useRef<HTMLElement>(null)
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (codeRef.current) {
|
|
21
|
-
Prism.highlightElement(codeRef.current)
|
|
22
|
-
}
|
|
23
|
-
}, [code, language])
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className="codeblock">
|
|
27
|
-
<pre>
|
|
28
|
-
<code ref={codeRef} className={`language-${language}`}>
|
|
29
|
-
{code}
|
|
30
|
-
</code>
|
|
31
|
-
</pre>
|
|
32
|
-
</div>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function Demo() {
|
|
37
|
-
const showInfoAlert = () => {
|
|
38
|
-
SweetestAlert({
|
|
39
|
-
type: "info",
|
|
40
|
-
title: "Information",
|
|
41
|
-
content: "This is an informational alert to let you know something.",
|
|
42
|
-
hideCancel: true,
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const showSuccessAlert = () => {
|
|
47
|
-
SweetestAlert({
|
|
48
|
-
type: "success",
|
|
49
|
-
title: "Success!",
|
|
50
|
-
content: "Your operation completed successfully.",
|
|
51
|
-
hideCancel: true,
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const showWarningAlert = () => {
|
|
56
|
-
SweetestAlert({
|
|
57
|
-
type: "warning",
|
|
58
|
-
title: "Warning",
|
|
59
|
-
content: "Are you sure you want to proceed with this action?",
|
|
60
|
-
confirm: () => console.log("User confirmed warning"),
|
|
61
|
-
cancel: () => console.log("User cancelled warning"),
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const showErrorAlert = () => {
|
|
66
|
-
SweetestAlert({
|
|
67
|
-
type: "error",
|
|
68
|
-
title: "Error Occurred",
|
|
69
|
-
content: "Something went wrong. Please try again.",
|
|
70
|
-
hideCancel: true,
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const showDangerAlert = () => {
|
|
75
|
-
SweetestAlert({
|
|
76
|
-
type: "danger",
|
|
77
|
-
title: "Delete Item",
|
|
78
|
-
content: "Are you sure you want to delete this item? This action cannot be undone.",
|
|
79
|
-
confirmButton: "Delete",
|
|
80
|
-
confirm: () => console.log("User confirmed deletion"),
|
|
81
|
-
cancel: () => console.log("User cancelled deletion"),
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const showCustomContentAlert = () => {
|
|
86
|
-
SweetestAlert({
|
|
87
|
-
type: "info",
|
|
88
|
-
title: "Custom Content",
|
|
89
|
-
content: (
|
|
90
|
-
<>
|
|
91
|
-
<p>You can pass custom React components as content.</p>
|
|
92
|
-
<ul>
|
|
93
|
-
<li>Item one</li>
|
|
94
|
-
<li>Item two</li>
|
|
95
|
-
<li>Item three</li>
|
|
96
|
-
</ul>
|
|
97
|
-
<p>This allows for rich, formatted content in your alerts.</p>
|
|
98
|
-
</>
|
|
99
|
-
),
|
|
100
|
-
confirm: () => console.log("Custom content confirmed"),
|
|
101
|
-
cancel: () => console.log("Custom content cancelled"),
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<>
|
|
107
|
-
<div className="container">
|
|
108
|
-
<h1>Sweetest Alert Demo</h1>
|
|
109
|
-
<h2>Click the buttons below to test different alert types</h2>
|
|
110
|
-
|
|
111
|
-
<div className="button-grid">
|
|
112
|
-
<button className="demo-button info" onClick={showInfoAlert}>
|
|
113
|
-
<h3>Info Alert</h3>
|
|
114
|
-
Simple notification
|
|
115
|
-
</button>
|
|
116
|
-
|
|
117
|
-
<button className="demo-button success" onClick={showSuccessAlert}>
|
|
118
|
-
<h3>Success Alert</h3>
|
|
119
|
-
Success message
|
|
120
|
-
</button>
|
|
121
|
-
|
|
122
|
-
<button className="demo-button warning" onClick={showWarningAlert}>
|
|
123
|
-
<h3>Warning Alert</h3>
|
|
124
|
-
With cancel option
|
|
125
|
-
</button>
|
|
126
|
-
|
|
127
|
-
<button className="demo-button error" onClick={showErrorAlert}>
|
|
128
|
-
<h3>Error Alert</h3>
|
|
129
|
-
Error notification
|
|
130
|
-
</button>
|
|
131
|
-
|
|
132
|
-
<button className="demo-button danger" onClick={showDangerAlert}>
|
|
133
|
-
<h3>Danger Alert</h3>
|
|
134
|
-
Destructive action
|
|
135
|
-
</button>
|
|
136
|
-
|
|
137
|
-
<button className="demo-button custom" onClick={showCustomContentAlert}>
|
|
138
|
-
<h3>Custom Content</h3>
|
|
139
|
-
Rich content example
|
|
140
|
-
</button>
|
|
141
|
-
</div>
|
|
142
|
-
|
|
143
|
-
<div className="tip">
|
|
144
|
-
<strong>Tip:</strong> Open your browser console to see the callback logs when you confirm or cancel alerts.
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
<div className="container">
|
|
149
|
-
<h1>Documentation</h1>
|
|
150
|
-
|
|
151
|
-
<h2>Installation</h2>
|
|
152
|
-
<CodeBlock code="yarn add @planningcenter/sweetest-alert" language="bash" />
|
|
153
|
-
|
|
154
|
-
<h3>Peer Dependencies</h3>
|
|
155
|
-
<p>This package requires the following peer dependencies:</p>
|
|
156
|
-
<ul>
|
|
157
|
-
<li>react ^18.3.1</li>
|
|
158
|
-
<li>react-dom ^18.3.1</li>
|
|
159
|
-
<li>@planningcenter/tapestry ^2.3.0</li>
|
|
160
|
-
<li>@planningcenter/icons ^15.25.0</li>
|
|
161
|
-
</ul>
|
|
162
|
-
|
|
163
|
-
<h2>Usage</h2>
|
|
164
|
-
|
|
165
|
-
<h3>Basic Example</h3>
|
|
166
|
-
<CodeBlock code={`import { SweetestAlert } from '@planningcenter/sweetest-alert'
|
|
167
|
-
|
|
168
|
-
// Simple notification
|
|
169
|
-
SweetestAlert({
|
|
170
|
-
title: "Warning!",
|
|
171
|
-
content: "Watch out, he's coming to get you!",
|
|
172
|
-
})`} />
|
|
173
|
-
|
|
174
|
-
<h3>Confirmation Dialog</h3>
|
|
175
|
-
<CodeBlock code={`SweetestAlert({
|
|
176
|
-
type: "danger",
|
|
177
|
-
title: "Delete Item",
|
|
178
|
-
content: "Are you sure you want to delete this item?",
|
|
179
|
-
confirm: () => console.log("Confirmed"),
|
|
180
|
-
cancel: () => console.log("Cancelled")
|
|
181
|
-
})`} />
|
|
182
|
-
|
|
183
|
-
<h3>Alert Types</h3>
|
|
184
|
-
<p>The component supports five visual types:</p>
|
|
185
|
-
<CodeBlock code={`// Info alert
|
|
186
|
-
SweetestAlert({
|
|
187
|
-
type: "info",
|
|
188
|
-
title: "Information",
|
|
189
|
-
content: "This is an informational message."
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// Success alert
|
|
193
|
-
SweetestAlert({
|
|
194
|
-
type: "success",
|
|
195
|
-
title: "Success!",
|
|
196
|
-
content: "Operation completed successfully."
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
// Warning alert (default)
|
|
200
|
-
SweetestAlert({
|
|
201
|
-
type: "warning",
|
|
202
|
-
title: "Warning",
|
|
203
|
-
content: "Please proceed with caution."
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
// Error alert
|
|
207
|
-
SweetestAlert({
|
|
208
|
-
type: "error",
|
|
209
|
-
title: "Error",
|
|
210
|
-
content: "Something went wrong."
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
// Danger alert (for destructive actions)
|
|
214
|
-
SweetestAlert({
|
|
215
|
-
type: "danger",
|
|
216
|
-
title: "Delete Account",
|
|
217
|
-
content: "This action cannot be undone.",
|
|
218
|
-
confirmButton: "Delete"
|
|
219
|
-
})`} />
|
|
220
|
-
|
|
221
|
-
<h3>Custom Content</h3>
|
|
222
|
-
<p>You can pass React components as content for rich formatting:</p>
|
|
223
|
-
<CodeBlock code={`SweetestAlert({
|
|
224
|
-
title: "Custom Content",
|
|
225
|
-
content: (
|
|
226
|
-
<>
|
|
227
|
-
<p>You can include any React elements:</p>
|
|
228
|
-
<ul>
|
|
229
|
-
<li>Lists</li>
|
|
230
|
-
<li>Links</li>
|
|
231
|
-
<li>Formatted text</li>
|
|
232
|
-
</ul>
|
|
233
|
-
</>
|
|
234
|
-
)
|
|
235
|
-
})`} />
|
|
236
|
-
|
|
237
|
-
<h2>API</h2>
|
|
238
|
-
<table>
|
|
239
|
-
<thead>
|
|
240
|
-
<tr>
|
|
241
|
-
<th>Parameter</th>
|
|
242
|
-
<th>Type</th>
|
|
243
|
-
<th>Required</th>
|
|
244
|
-
<th>Default</th>
|
|
245
|
-
<th>Description</th>
|
|
246
|
-
</tr>
|
|
247
|
-
</thead>
|
|
248
|
-
<tbody>
|
|
249
|
-
<tr>
|
|
250
|
-
<td><code>title</code></td>
|
|
251
|
-
<td><code>string</code></td>
|
|
252
|
-
<td>Yes</td>
|
|
253
|
-
<td>-</td>
|
|
254
|
-
<td>The main heading text displayed at the top of the modal</td>
|
|
255
|
-
</tr>
|
|
256
|
-
<tr>
|
|
257
|
-
<td><code>content</code></td>
|
|
258
|
-
<td><code>string | React.ReactNode</code></td>
|
|
259
|
-
<td>Yes</td>
|
|
260
|
-
<td>-</td>
|
|
261
|
-
<td>The body content. Accepts plain text or React components</td>
|
|
262
|
-
</tr>
|
|
263
|
-
<tr>
|
|
264
|
-
<td><code>type</code></td>
|
|
265
|
-
<td><code>"info" | "success" | "error" | "danger" | "warning"</code></td>
|
|
266
|
-
<td>No</td>
|
|
267
|
-
<td>"warning"</td>
|
|
268
|
-
<td>The visual type of alert, affects icon and styling</td>
|
|
269
|
-
</tr>
|
|
270
|
-
<tr>
|
|
271
|
-
<td><code>confirm</code></td>
|
|
272
|
-
<td><code>() => void</code></td>
|
|
273
|
-
<td>No</td>
|
|
274
|
-
<td>-</td>
|
|
275
|
-
<td>Callback executed when confirm button is clicked</td>
|
|
276
|
-
</tr>
|
|
277
|
-
<tr>
|
|
278
|
-
<td><code>cancel</code></td>
|
|
279
|
-
<td><code>() => void</code></td>
|
|
280
|
-
<td>No</td>
|
|
281
|
-
<td>-</td>
|
|
282
|
-
<td>Callback executed when cancel button is clicked</td>
|
|
283
|
-
</tr>
|
|
284
|
-
<tr>
|
|
285
|
-
<td><code>confirmButton</code></td>
|
|
286
|
-
<td><code>string</code></td>
|
|
287
|
-
<td>No</td>
|
|
288
|
-
<td>"Okay"</td>
|
|
289
|
-
<td>Custom text for the confirm button</td>
|
|
290
|
-
</tr>
|
|
291
|
-
<tr>
|
|
292
|
-
<td><code>hideCancel</code></td>
|
|
293
|
-
<td><code>boolean</code></td>
|
|
294
|
-
<td>No</td>
|
|
295
|
-
<td>false</td>
|
|
296
|
-
<td>When true, hides the cancel button for simple notifications</td>
|
|
297
|
-
</tr>
|
|
298
|
-
</tbody>
|
|
299
|
-
</table>
|
|
300
|
-
</div>
|
|
301
|
-
</>
|
|
302
|
-
)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const root = createRoot(document.getElementById("root")!)
|
|
306
|
-
root.render(<Demo />)
|
package/docs/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍬</text></svg>" />
|
|
7
|
-
<title>Sweetest Alert Demo</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root"></div>
|
|
11
|
-
<script type="module" src="./demo.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
package/eslint.config.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js';
|
|
2
|
-
import tseslint from 'typescript-eslint';
|
|
3
|
-
import react from 'eslint-plugin-react';
|
|
4
|
-
import reactHooks from 'eslint-plugin-react-hooks';
|
|
5
|
-
|
|
6
|
-
export default tseslint.config(
|
|
7
|
-
js.configs.recommended,
|
|
8
|
-
...tseslint.configs.recommended,
|
|
9
|
-
{
|
|
10
|
-
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
11
|
-
plugins: {
|
|
12
|
-
react,
|
|
13
|
-
'react-hooks': reactHooks,
|
|
14
|
-
},
|
|
15
|
-
languageOptions: {
|
|
16
|
-
parserOptions: {
|
|
17
|
-
ecmaFeatures: {
|
|
18
|
-
jsx: true,
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
settings: {
|
|
23
|
-
react: {
|
|
24
|
-
version: 'detect',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
rules: {
|
|
28
|
-
...react.configs.recommended.rules,
|
|
29
|
-
...react.configs['jsx-runtime'].rules,
|
|
30
|
-
...reactHooks.configs.recommended.rules,
|
|
31
|
-
'eqeqeq': ['error', 'always'],
|
|
32
|
-
'no-console': 'warn',
|
|
33
|
-
'no-underscore-dangle': 'error',
|
|
34
|
-
'no-unused-vars': 'off',
|
|
35
|
-
'@typescript-eslint/no-unused-vars': ['error', {
|
|
36
|
-
'argsIgnorePattern': '^_',
|
|
37
|
-
'varsIgnorePattern': '^_',
|
|
38
|
-
}],
|
|
39
|
-
'react/no-deprecated': 'error',
|
|
40
|
-
'semi': ['error', 'never'],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
files: ['tests/**/*.{js,jsx,ts,tsx}'],
|
|
45
|
-
languageOptions: {
|
|
46
|
-
globals: {
|
|
47
|
-
document: 'readonly',
|
|
48
|
-
window: 'readonly',
|
|
49
|
-
Event: 'readonly',
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
ignores: ['dist/', 'node_modules/', '*.config.ts', '*.config.js'],
|
|
55
|
-
}
|
|
56
|
-
);
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { SweetestAlert } from "./sweetest_alert.tsx"
|
package/src/sweetest_alert.css
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
.pco-sweetest-alert {
|
|
2
|
-
background: var(--t-surface-color-card);
|
|
3
|
-
border: 0;
|
|
4
|
-
border-radius: var(--t-border-radius-lg);
|
|
5
|
-
box-shadow: 0 0 var(--t-spacing-3) var(--t-fill-color-transparency-dark-020);
|
|
6
|
-
font-size: var(--t-font-size-md);
|
|
7
|
-
margin: auto;
|
|
8
|
-
max-width: 600px;
|
|
9
|
-
padding: var(--t-spacing-4) var(--t-spacing-4) 0;
|
|
10
|
-
text-align: center;
|
|
11
|
-
|
|
12
|
-
&::backdrop {
|
|
13
|
-
background: var(--t-fill-color-transparency-dark-040);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.pco-sweetest-alert__icon {
|
|
18
|
-
height: var(--t-element-size-3xl);
|
|
19
|
-
margin-block: var(--t-spacing-1);
|
|
20
|
-
width: var(--t-element-size-3xl);
|
|
21
|
-
|
|
22
|
-
&.pco-sweetest-alert--info {
|
|
23
|
-
fill: var(--t-icon-color-status-info-primary);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
&.pco-sweetest-alert--success {
|
|
27
|
-
fill: var(--t-icon-color-status-success-primary);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
&.pco-sweetest-alert--warning {
|
|
31
|
-
fill: var(--t-icon-color-status-warning-primary);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
&:is(.pco-sweetest-alert--danger, .pco-sweetest-alert--error) {
|
|
35
|
-
fill: var(--t-icon-color-status-error-primary);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.pco-sweetest-alert__title {
|
|
40
|
-
color: var(--t-text-color-default-headline);
|
|
41
|
-
font-size: var(--t-font-size-2xl);
|
|
42
|
-
font-weight: var(--t-font-weight-semibold);
|
|
43
|
-
margin-block: var(--t-spacing-2);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.pco-sweetest-alert__body {
|
|
47
|
-
color: var(--t-text-color-default-primary);
|
|
48
|
-
font-weight: var(--t-font-weight-normal);
|
|
49
|
-
line-height: 1.4;
|
|
50
|
-
margin-bottom: var(--t-spacing-4);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.pco-sweetest-alert__actions {
|
|
54
|
-
align-items: center;
|
|
55
|
-
border-top: var(--t-border-width-default) solid var(--t-border-color-default-base);
|
|
56
|
-
display: flex;
|
|
57
|
-
flex-wrap: wrap;
|
|
58
|
-
gap: var(--t-spacing-1);
|
|
59
|
-
justify-content: center;
|
|
60
|
-
margin-inline: calc(-1 * var(--t-spacing-4));
|
|
61
|
-
padding: var(--t-spacing-3) var(--t-spacing-4);
|
|
62
|
-
}
|
package/src/sweetest_alert.tsx
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { createRoot } from "react-dom/client"
|
|
3
|
-
import { Button } from "@planningcenter/tapestry"
|
|
4
|
-
import {
|
|
5
|
-
checkCircle,
|
|
6
|
-
xCircle,
|
|
7
|
-
infoCircle,
|
|
8
|
-
exclamationTriangle,
|
|
9
|
-
} from "@planningcenter/icons/paths/general"
|
|
10
|
-
|
|
11
|
-
// A modal dialog component that replaces SweetAlert2 with a custom implementation.
|
|
12
|
-
// Creates and manages its own DOM dialog element with React rendering.
|
|
13
|
-
//
|
|
14
|
-
// Basic Usage
|
|
15
|
-
//
|
|
16
|
-
// SweetestAlert({
|
|
17
|
-
// title: "Delete Item",
|
|
18
|
-
// content: "Are you sure you want to delete this item?",
|
|
19
|
-
// confirm: () => console.log("Confirmed"),
|
|
20
|
-
// cancel: () => console.log("Cancelled")
|
|
21
|
-
// });
|
|
22
|
-
|
|
23
|
-
type AlertTypes = "info" | "success" | "error" | "danger" | "warning"
|
|
24
|
-
|
|
25
|
-
export function SweetestAlert({
|
|
26
|
-
// Optional: Callback function executed when cancel button is clicked
|
|
27
|
-
cancel,
|
|
28
|
-
|
|
29
|
-
// Optional: Callback function executed when confirm button is clicked
|
|
30
|
-
confirm,
|
|
31
|
-
|
|
32
|
-
// Optional: Text for the confirm button. Defaults to "Okay".
|
|
33
|
-
confirmButton = "Okay",
|
|
34
|
-
|
|
35
|
-
// Required: The body content explaining the alert.
|
|
36
|
-
// Accepts a React fragment or a string of text.
|
|
37
|
-
content,
|
|
38
|
-
|
|
39
|
-
// Optional: When true, hides the cancel button for simple notifications.
|
|
40
|
-
// Defaults to "false".
|
|
41
|
-
hideCancel = false,
|
|
42
|
-
|
|
43
|
-
// Required: The main heading text displayed at the top of the modal
|
|
44
|
-
title,
|
|
45
|
-
|
|
46
|
-
// Optional: The visual type of alert, affects icon and styling.
|
|
47
|
-
// Values can be "info", "success", "error", "danger", or "warning" (default).
|
|
48
|
-
type = "warning",
|
|
49
|
-
}: {
|
|
50
|
-
cancel?: () => void
|
|
51
|
-
confirm?: () => void
|
|
52
|
-
confirmButton?: string
|
|
53
|
-
content: string | React.ReactNode
|
|
54
|
-
hideCancel?: boolean
|
|
55
|
-
title: string
|
|
56
|
-
type?: AlertTypes
|
|
57
|
-
}) {
|
|
58
|
-
const dialog = document.createElement("dialog")
|
|
59
|
-
dialog.className = "pco-sweetest-alert"
|
|
60
|
-
document.body.appendChild(dialog)
|
|
61
|
-
const root = createRoot(dialog)
|
|
62
|
-
|
|
63
|
-
const cleanup = () => {
|
|
64
|
-
root.unmount()
|
|
65
|
-
document.body.removeChild(dialog)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const handleConfirm = () => {
|
|
69
|
-
cleanup()
|
|
70
|
-
confirm?.()
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const handleCancel = () => {
|
|
74
|
-
cleanup()
|
|
75
|
-
cancel?.()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const iconPath = () => {
|
|
79
|
-
switch (type) {
|
|
80
|
-
case "success":
|
|
81
|
-
return checkCircle
|
|
82
|
-
case "error":
|
|
83
|
-
return xCircle
|
|
84
|
-
case "info":
|
|
85
|
-
return infoCircle
|
|
86
|
-
case "danger":
|
|
87
|
-
case "warning":
|
|
88
|
-
return exclamationTriangle
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
root.render(
|
|
93
|
-
<>
|
|
94
|
-
<svg
|
|
95
|
-
className={`pco-sweetest-alert__icon pco-sweetest-alert--${type}`}
|
|
96
|
-
width="48"
|
|
97
|
-
height="48"
|
|
98
|
-
viewBox="0 0 16 16"
|
|
99
|
-
aria-hidden="true"
|
|
100
|
-
>
|
|
101
|
-
<path d={iconPath()} />
|
|
102
|
-
</svg>
|
|
103
|
-
|
|
104
|
-
<h2 className="pco-sweetest-alert__title">{title}</h2>
|
|
105
|
-
|
|
106
|
-
<div className="pco-sweetest-alert__body">
|
|
107
|
-
{typeof content === "string" ? <p>{content}</p> : content}
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
<div className="pco-sweetest-alert__actions">
|
|
111
|
-
{!hideCancel && <Button label="Cancel" kind="ghost" onClick={handleCancel} />}
|
|
112
|
-
<Button
|
|
113
|
-
label={confirmButton}
|
|
114
|
-
kind={type === "danger" ? "delete" : "primary"}
|
|
115
|
-
onClick={handleConfirm}
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
118
|
-
</>
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
dialog.showModal()
|
|
122
|
-
|
|
123
|
-
dialog.addEventListener("close", handleCancel)
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
show: () => dialog.showModal(),
|
|
127
|
-
close: () => dialog.close(),
|
|
128
|
-
cleanup,
|
|
129
|
-
}
|
|
130
|
-
}
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { screen, fireEvent, waitFor, act } from "@testing-library/react"
|
|
3
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
|
|
4
|
-
import { SweetestAlert } from "../src/sweetest_alert.tsx"
|
|
5
|
-
|
|
6
|
-
describe("SweetestAlert", () => {
|
|
7
|
-
const mockConfirm = vi.fn()
|
|
8
|
-
const mockCancel = vi.fn()
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
vi.clearAllMocks()
|
|
12
|
-
document.querySelectorAll("dialog.pco-sweetest-alert").forEach((dialog) => dialog.remove())
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
document.querySelectorAll("dialog.pco-sweetest-alert").forEach((dialog) => dialog.remove())
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
describe("Initial Render", () => {
|
|
20
|
-
beforeEach(async () => {
|
|
21
|
-
await act(async () => {
|
|
22
|
-
SweetestAlert({
|
|
23
|
-
title: "Test Title",
|
|
24
|
-
content: "Test content",
|
|
25
|
-
confirm: mockConfirm,
|
|
26
|
-
cancel: mockCancel,
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("creates and displays a modal dialog", () => {
|
|
32
|
-
const dialog = document.querySelector("dialog.pco-sweetest-alert")
|
|
33
|
-
expect(dialog).toBeInTheDocument()
|
|
34
|
-
expect(dialog).toHaveAttribute("open")
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it("displays the provided title as a heading", () => {
|
|
38
|
-
expect(screen.getByRole("heading", { name: "Test Title" })).toBeInTheDocument()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it("displays the content text in the dialog body", () => {
|
|
42
|
-
expect(screen.getByText("Test content")).toBeInTheDocument()
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it("displays a cancel button", () => {
|
|
46
|
-
expect(screen.getByRole("button", { name: "Cancel" })).toBeInTheDocument()
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it("displays a confirm button with default label", () => {
|
|
50
|
-
expect(screen.getByRole("button", { name: "Okay" })).toBeInTheDocument()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it("displays a warning icon when no type is specified", () => {
|
|
54
|
-
const icon = document.querySelector(".pco-sweetest-alert__icon.pco-sweetest-alert--warning")
|
|
55
|
-
expect(icon).toBeInTheDocument()
|
|
56
|
-
expect(icon.querySelector("path")).toBeInTheDocument()
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe("Alert Types", () => {
|
|
61
|
-
it("displays success icon when type is set to success", async () => {
|
|
62
|
-
await act(async () => {
|
|
63
|
-
SweetestAlert({
|
|
64
|
-
title: "Success",
|
|
65
|
-
content: "Success content",
|
|
66
|
-
type: "success",
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const icon = document.querySelector(".pco-sweetest-alert__icon.pco-sweetest-alert--success")
|
|
71
|
-
expect(icon).toBeInTheDocument()
|
|
72
|
-
expect(icon.querySelector("path")).toBeInTheDocument()
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it("displays error icon when type is set to error", async () => {
|
|
76
|
-
await act(async () => {
|
|
77
|
-
SweetestAlert({
|
|
78
|
-
title: "Error",
|
|
79
|
-
content: "Error content",
|
|
80
|
-
type: "error",
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
const icon = document.querySelector(".pco-sweetest-alert__icon.pco-sweetest-alert--error")
|
|
85
|
-
expect(icon).toBeInTheDocument()
|
|
86
|
-
expect(icon.querySelector("path")).toBeInTheDocument()
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it("displays info icon when type is set to info", async () => {
|
|
90
|
-
await act(async () => {
|
|
91
|
-
SweetestAlert({
|
|
92
|
-
title: "Info",
|
|
93
|
-
content: "Info content",
|
|
94
|
-
type: "info",
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
const icon = document.querySelector(".pco-sweetest-alert__icon.pco-sweetest-alert--info")
|
|
99
|
-
expect(icon).toBeInTheDocument()
|
|
100
|
-
expect(icon.querySelector("path")).toBeInTheDocument()
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it("displays red warning icon and delete button kind when type is set to danger", async () => {
|
|
104
|
-
await act(async () => {
|
|
105
|
-
SweetestAlert({
|
|
106
|
-
title: "Danger",
|
|
107
|
-
content: "Danger content",
|
|
108
|
-
type: "danger",
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const icon = document.querySelector(".pco-sweetest-alert__icon.pco-sweetest-alert--danger")
|
|
113
|
-
expect(icon).toBeInTheDocument()
|
|
114
|
-
expect(icon.querySelector("path")).toBeInTheDocument()
|
|
115
|
-
|
|
116
|
-
const button = document.querySelector(".tds-btn.tds-btn--delete")
|
|
117
|
-
expect(button).toBeInTheDocument()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe("Button Interactions", () => {
|
|
122
|
-
beforeEach(async () => {
|
|
123
|
-
await act(async () => {
|
|
124
|
-
SweetestAlert({
|
|
125
|
-
title: "Interactive Test",
|
|
126
|
-
content: "Test content",
|
|
127
|
-
confirm: mockConfirm,
|
|
128
|
-
cancel: mockCancel,
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it("executes confirm callback and removes dialog when confirm button is clicked", async () => {
|
|
134
|
-
const confirmButton = screen.getByRole("button", { name: "Okay" })
|
|
135
|
-
fireEvent.click(confirmButton)
|
|
136
|
-
|
|
137
|
-
await waitFor(() => {
|
|
138
|
-
expect(mockConfirm).toHaveBeenCalledTimes(1)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
// Dialog should be removed from DOM
|
|
142
|
-
await waitFor(() => {
|
|
143
|
-
expect(document.querySelector("dialog.pco-sweetest-alert")).not.toBeInTheDocument()
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it("executes cancel callback and removes dialog when cancel button is clicked", async () => {
|
|
148
|
-
const cancelButton = screen.getByRole("button", { name: "Cancel" })
|
|
149
|
-
fireEvent.click(cancelButton)
|
|
150
|
-
|
|
151
|
-
await waitFor(() => {
|
|
152
|
-
expect(mockCancel).toHaveBeenCalledTimes(1)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
// Dialog should be removed from DOM
|
|
156
|
-
await waitFor(() => {
|
|
157
|
-
expect(document.querySelector("dialog.pco-sweetest-alert")).not.toBeInTheDocument()
|
|
158
|
-
})
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it("executes cancel callback and removes dialog when escape key is pressed", async () => {
|
|
162
|
-
const dialog = document.querySelector("dialog.pco-sweetest-alert")
|
|
163
|
-
|
|
164
|
-
// Simulate pressing Escape key which triggers the dialog's close event
|
|
165
|
-
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" })
|
|
166
|
-
|
|
167
|
-
// Manually trigger the close event since jsdom doesn't handle native dialog behavior
|
|
168
|
-
fireEvent(dialog, new Event("close"))
|
|
169
|
-
|
|
170
|
-
await waitFor(() => {
|
|
171
|
-
expect(mockCancel).toHaveBeenCalledTimes(1)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
// Dialog should be removed from DOM
|
|
175
|
-
await waitFor(() => {
|
|
176
|
-
expect(document.querySelector("dialog.pco-sweetest-alert")).not.toBeInTheDocument()
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
describe("Custom Button Labels", () => {
|
|
182
|
-
beforeEach(async () => {
|
|
183
|
-
await act(async () => {
|
|
184
|
-
SweetestAlert({
|
|
185
|
-
title: "Custom Buttons",
|
|
186
|
-
content: "Test content",
|
|
187
|
-
confirmButton: "Yes please",
|
|
188
|
-
confirm: mockConfirm,
|
|
189
|
-
cancel: mockCancel,
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it("displays custom confirm button text when confirmButton prop is provided", () => {
|
|
195
|
-
expect(screen.getByRole("button", { name: "Yes please" })).toBeInTheDocument()
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe("Hide Cancel Button", () => {
|
|
200
|
-
beforeEach(async () => {
|
|
201
|
-
await act(async () => {
|
|
202
|
-
SweetestAlert({
|
|
203
|
-
title: "Custom Buttons",
|
|
204
|
-
content: "Test content",
|
|
205
|
-
confirm: mockConfirm,
|
|
206
|
-
cancel: mockCancel,
|
|
207
|
-
hideCancel: true,
|
|
208
|
-
})
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it("does not display cancel button when hideCancel is true", () => {
|
|
213
|
-
expect(screen.queryByRole("button", { name: "Cancel" })).not.toBeInTheDocument()
|
|
214
|
-
})
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
describe("Renders HTML instead of text content", () => {
|
|
218
|
-
it("renders content property as HTML", async () => {
|
|
219
|
-
await act(async () => {
|
|
220
|
-
const HtmlFrag = () => {
|
|
221
|
-
return <strong>Test content</strong>
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
SweetestAlert({
|
|
225
|
-
title: "Custom HTML",
|
|
226
|
-
content: <HtmlFrag />,
|
|
227
|
-
confirm: mockConfirm,
|
|
228
|
-
cancel: mockCancel,
|
|
229
|
-
})
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
expect(screen.getByText("Test content")).toBeInTheDocument()
|
|
233
|
-
expect(screen.getByText("Test content").tagName).toBe("STRONG")
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
it("does not render content property as HTML", async () => {
|
|
237
|
-
await act(async () => {
|
|
238
|
-
SweetestAlert({
|
|
239
|
-
title: "Custom HTML",
|
|
240
|
-
content: "<strong>Test content</strong>",
|
|
241
|
-
confirm: mockConfirm,
|
|
242
|
-
cancel: mockCancel,
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
// Test that escaped HTML is displayed
|
|
247
|
-
expect(screen.getByText("<strong>Test content</strong>")).toBeInTheDocument()
|
|
248
|
-
|
|
249
|
-
// Test that actual HTML element is NOT created by checking there's no strong element
|
|
250
|
-
const strongElement = document.querySelector("strong")
|
|
251
|
-
expect(strongElement).not.toBeInTheDocument()
|
|
252
|
-
})
|
|
253
|
-
})
|
|
254
|
-
})
|
package/tests/test-setup.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom'
|
|
2
|
-
import { vi, beforeAll } from 'vitest'
|
|
3
|
-
|
|
4
|
-
// Mock dialog.showModal() method which is not available in jsdom
|
|
5
|
-
beforeAll(() => {
|
|
6
|
-
HTMLDialogElement.prototype.showModal = vi.fn(function (this: HTMLDialogElement) {
|
|
7
|
-
this.open = true
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
11
|
-
this.open = false
|
|
12
|
-
})
|
|
13
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es2024",
|
|
4
|
-
"module": "esnext",
|
|
5
|
-
"lib": ["es2024", "DOM", "DOM.Iterable"],
|
|
6
|
-
"jsx": "react-jsx",
|
|
7
|
-
"moduleResolution": "bundler",
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"allowImportingTsExtensions": true,
|
|
10
|
-
"allowJs": true,
|
|
11
|
-
"checkJs": false,
|
|
12
|
-
"strict": true,
|
|
13
|
-
"noUnusedLocals": true,
|
|
14
|
-
"noUnusedParameters": true,
|
|
15
|
-
"noFallthroughCasesInSwitch": true,
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
"esModuleInterop": true,
|
|
18
|
-
"skipLibCheck": true,
|
|
19
|
-
"forceConsistentCasingInFileNames": true
|
|
20
|
-
},
|
|
21
|
-
"include": ["src/**/*", "tests/sweetest_alert.test.jsx", "tests/test-setup.ts"],
|
|
22
|
-
"exclude": ["node_modules"]
|
|
23
|
-
}
|
package/vite.config.ts
DELETED
package/vitest.config.ts
DELETED