PluginProbe
Extendify / 0.11.1
Extendify v0.11.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Onboarding / components / ExitModal.js

ExitModal.js in Extendify 0.11.1, at src/Onboarding/components/ExitModal.js

205 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Button } from '@wordpress/components'
2 import { useRef, useState, useEffect } from '@wordpress/element'
3 import { __ } from '@wordpress/i18n'
4 import { Icon, close } from '@wordpress/icons'
5 import { Dialog } from '@headlessui/react'
6 import classNames from 'classnames'
7 import { shuffle } from 'lodash'
8 import { getExitQuestions } from '@onboarding/api/DataApi'
9 import { updateOption } from '@onboarding/api/WPApi'
10 import { useGlobalStore } from '@onboarding/state/Global'
11 import { useUserSelectionStore } from '@onboarding/state/UserSelections'
12 import { Checkmark } from '@onboarding/svg'
13
14 export const ExitModal = () => {
15 const { exitModalOpen, closeExitModal, hoveredOverExitButton } =
16 useGlobalStore()
17 const { setExitFeedback } = useUserSelectionStore()
18 const [value, setValue] = useState()
19 const [options, setOptions] = useState([])
20 const initialFocus = useRef()
21 const closeLaunch = async () => {
22 // Store when Launch is skipped.
23 await updateOption(
24 'extendify_onboarding_skipped',
25 new Date().toISOString(),
26 )
27 location.href = window.extOnbData.adminUrl
28 }
29
30 useEffect(() => {
31 if (!hoveredOverExitButton) return
32 // Intentionally not using SWR so we only try once.
33 getExitQuestions()
34 .then(({ data }) => {
35 if (!Array.isArray(data) || !data[0]?.key) {
36 throw new Error('Invalid data')
37 }
38 setOptions([
39 ...shuffle(data.filter((d) => d.key !== 'Other')),
40 { key: 'Other', label: __('Other', 'extendify') },
41 ])
42 })
43 .catch(() => {
44 const backupQuestions = [
45 {
46 key: 'I still want it, just disabling temporary',
47 label: __(
48 'I still want it, just disabling temporary',
49 'extendify',
50 ),
51 },
52 {
53 key: 'I plan on using my own theme or builder',
54 label: __(
55 'I plan on using my own theme or builder',
56 'extendify',
57 ),
58 },
59 {
60 key: "The theme designs don't look great",
61 label: __(
62 "The theme designs don't look great",
63 'extendify',
64 ),
65 },
66 ]
67 setOptions([
68 ...shuffle(backupQuestions),
69 { key: 'Other', label: __('Other', 'extendify') },
70 ])
71 })
72 .finally(() => {
73 initialFocus.current?.focus()
74 })
75 }, [hoveredOverExitButton])
76
77 useEffect(() => {
78 setExitFeedback(value)
79 }, [value, setExitFeedback])
80
81 return (
82 <Dialog
83 as="div"
84 className="extendify-onboarding"
85 open={exitModalOpen}
86 initialFocus={initialFocus}
87 onClose={closeExitModal}>
88 <div className="absolute top-0 mx-auto w-full h-full overflow-hidden p-2 md:p-6 md:flex justify-center items-center z-max">
89 <div
90 className="fixed inset-0 bg-black bg-opacity-40 transition-opacity"
91 aria-hidden="true"
92 />
93 <Dialog.Title className="sr-only">
94 {__('Exit Launch')}
95 </Dialog.Title>
96 <form
97 onSubmit={closeLaunch}
98 style={{ maxWidth: '400px' }}
99 className="sm:flex relative shadow-2xl sm:overflow-hidden mx-auto bg-white flex flex-col p-8">
100 <Button
101 className="absolute top-0 right-0"
102 onClick={closeExitModal}
103 icon={<Icon icon={close} size={24} />}
104 label={__('Exit Launch', 'extendify')}
105 />
106 <p className="m-0 text-lg font-bold text-left">
107 {__(
108 'Thanks for trying Extendify Launch. How can we make this better?',
109 'extendify',
110 )}
111 </p>
112 <div
113 role="radiogroup"
114 className="flex flex-col text-base mt-4">
115 {options.map(({ key, label }, i) => (
116 <LabeledCheckbox
117 initialFocus={i ? undefined : initialFocus}
118 key={key}
119 slug={key}
120 label={label}
121 value={value}
122 setValue={(v) =>
123 setValue((previousValue) =>
124 previousValue === v ? null : v,
125 )
126 }
127 />
128 ))}
129 </div>
130 <div className="flex justify-end mt-8">
131 {/* If they've made a choice, we are sending it in real time */}
132 {value ? null : (
133 <button
134 className="px-4 py-3 mr-4 button-focus"
135 type="button"
136 onClick={closeLaunch}>
137 {__('Skip', 'extendify')}
138 </button>
139 )}
140 <button
141 className="px-4 py-3 font-bold bg-partner-primary-bg text-partner-primary-text button-focus"
142 type="button"
143 onClick={closeLaunch}>
144 {__('Submit', 'extendify')}
145 </button>
146 </div>
147 </form>
148 </div>
149 </Dialog>
150 )
151 }
152
153 const LabeledCheckbox = ({ label, slug, setValue, value, initialFocus }) => {
154 const { setExitFeedback } = useUserSelectionStore()
155 const needsInput = slug === 'Other'
156 const checked = value === slug
157 const id = slug
158 .toLowerCase()
159 .replace(/ /g, '-')
160 .replace(/[^\w-]+/g, '')
161
162 return (
163 <>
164 <span className="flex items-center leading-loose">
165 <span
166 onClick={() => setValue(slug)}
167 onKeyDown={(e) => {
168 if (e.key === 'Enter' || e.key === ' ') {
169 setValue(slug)
170 }
171 }}
172 role="radio"
173 aria-labelledby={id}
174 aria-checked={checked}
175 data-value={slug}
176 // Will focus on the first element in the list
177 ref={initialFocus}
178 tabIndex={0}
179 className="w-5 h-5 relative mr-2">
180 <span className="h-5 w-5 rounded-sm m-0 block border border-gray-900 button-focus" />
181 <Checkmark
182 className={classNames('absolute -top-0.5 -left-0.5', {
183 'text-partner-primary-bg': checked,
184 })}
185 style={{ width: 24, color: '#fff' }}
186 role="presentation"
187 />
188 </span>
189 <span onClick={() => setValue(slug)} id={id}>
190 {label}
191 </span>
192 </span>
193 {needsInput && checked ? (
194 <textarea
195 onChange={(e) =>
196 setExitFeedback(`Other: ${e.target.value}`)
197 }
198 className="border border-gray-400 mt-2 text-base p-2"
199 rows="4"
200 />
201 ) : null}
202 </>
203 )
204 }
205