mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-03 12:21:05 -05:00
pmf widget: add focus trap, make radio buttons accesible via keyboard
This commit is contained in:
@@ -17,14 +17,12 @@
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@floating-ui/dom": "^1.1.0",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"eslint": "^8.32.0",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"focus-trap": "^7.2.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"microbundle": "^0.15.1",
|
||||
"tsup": "^6.4.0",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -95,6 +95,10 @@
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.formbricks-next-button:focus {
|
||||
box-shadow: 0 0 0 3px var(--formbricksPmf-brand-color-transparent);
|
||||
}
|
||||
|
||||
/* Textarea */
|
||||
|
||||
.formbricks-textarea {
|
||||
@@ -130,7 +134,9 @@
|
||||
}
|
||||
|
||||
.formbricks-radio-input {
|
||||
display: none;
|
||||
width: 0;
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.formbricks-radio-label {
|
||||
@@ -150,6 +156,10 @@
|
||||
background-color: var(--formbricksPmf-brand-color);
|
||||
}
|
||||
|
||||
.formbricks-radio-input:focus + .formbricks-radio-label {
|
||||
box-shadow: 0 0 0 3px var(--formbricksPmf-brand-color-transparent);
|
||||
}
|
||||
|
||||
.formbricks-radio-input:checked + label {
|
||||
background-color: #58ba83;
|
||||
}
|
||||
|
||||
@@ -7,26 +7,35 @@
|
||||
<fieldset class="formbricks-radio-fieldset">
|
||||
<legend class="formbricks-sr-only">Disappointment options</legend>
|
||||
<div class="formbricks-radio-options">
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="veryDisappointed">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">very disappointed </label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
id="formbricksPmf-0-0"
|
||||
name="formbricksPmf-0-0"
|
||||
type="radio"
|
||||
class="formbricks-radio-input"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="veryDisappointed" />
|
||||
<label class="formbricks-radio-label" for="formbricksPmf-0-0">very disappointed </label>
|
||||
</div>
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="somewhatDisappointed">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">somewhat disappointed </label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
id="formbricksPmf-0-1"
|
||||
name="formbricksPmf-0-1"
|
||||
type="radio"
|
||||
class="formbricks-radio-input"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="somewhatDisappointed" />
|
||||
<label class="formbricks-radio-label" for="formbricksPmf-0-1">somewhat disappointed </label>
|
||||
</div>
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="notDisappointed">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">not disappointed </label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
id="formbricksPmf-0-2"
|
||||
name="formbricksPmf-0-2"
|
||||
type="radio"
|
||||
class="formbricks-radio-input"
|
||||
data-element-name="disappointment"
|
||||
data-element-value="notDisappointed" />
|
||||
<label class="formbricks-radio-label" for="formbricksPmf-0-2">not disappointed </label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -35,7 +44,7 @@
|
||||
<div class="formbricks-element formbricks-hidden" id="formbricks-question-1">
|
||||
<p class="formbricks-element-label">What is the main benefit you receive from our service?</p>
|
||||
<form class="formbricks-form" data-element-name="mainBenefit">
|
||||
<textarea rows="4" name="mainBenefit" class="formbricks-textarea" required></textarea>
|
||||
<textarea rows="4" name="mainBenefit" class="formbricks-textarea" required autofocus></textarea>
|
||||
<div class="formbricks-next-button-wrapper">
|
||||
<button type="submit" class="formbricks-next-button">Next</button>
|
||||
</div>
|
||||
@@ -47,34 +56,55 @@
|
||||
<fieldset class="formbricks-radio-fieldset">
|
||||
<legend class="formbricks-sr-only">Job title options</legend>
|
||||
<div class="formbricks-radio-options">
|
||||
<div class="formbricks-radio-option" data-element-name="userSegment" data-element-value="founder">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">Founder</label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
data-element-name="userSegment"
|
||||
data-element-value="founder"
|
||||
id="formbricksPmf-2-0"
|
||||
name="formbricksPmf-2-0"
|
||||
type="radio"
|
||||
class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label" for="formbricksPmf-2-0">Founder</label>
|
||||
</div>
|
||||
<div class="formbricks-radio-option" data-element-name="userSegment" data-element-value="executive">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">Executive</label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
data-element-name="userSegment"
|
||||
data-element-value="executive"
|
||||
id="formbricksPmf-2-1"
|
||||
name="formbricksPmf-2-1"
|
||||
type="radio"
|
||||
class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label" id="formbricksPmf-2-1">Executive</label>
|
||||
</div>
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="userSegment"
|
||||
data-element-value="productManager">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">Product Manager</label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
data-element-name="userSegment"
|
||||
data-element-value="productManager"
|
||||
id="formbricksPmf-2-2"
|
||||
name="formbricksPmf-2-2"
|
||||
type="radio"
|
||||
class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label" id="formbricksPmf-2-2">Product Manager</label>
|
||||
</div>
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="userSegment"
|
||||
data-element-value="productOwner">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">Product Owner</label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
data-element-name="userSegment"
|
||||
data-element-value="productOwner"
|
||||
id="formbricksPmf-2-3"
|
||||
name="formbricksPmf-2-3"
|
||||
type="radio"
|
||||
class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label" id="formbricksPmf-2-3">Product Owner</label>
|
||||
</div>
|
||||
<div
|
||||
class="formbricks-radio-option"
|
||||
data-element-name="userSegment"
|
||||
data-element-value="softwareEngineer">
|
||||
<input name="notification-method" type="radio" class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label">Software Engineer</label>
|
||||
<div class="formbricks-radio-option">
|
||||
<input
|
||||
data-element-name="userSegment"
|
||||
data-element-value="softwareEngineer"
|
||||
id="formbricksPmf-2-4"
|
||||
name="formbricksPmf-2-4"
|
||||
type="radio"
|
||||
class="formbricks-radio-input" />
|
||||
<label class="formbricks-radio-label" id="formbricksPmf-2-4">Software Engineer</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -83,7 +113,7 @@
|
||||
<div class="formbricks-element formbricks-hidden" id="formbricks-question-3">
|
||||
<p class="formbricks-element-label">How can we improve our service for you?</p>
|
||||
<form class="formbricks-form" data-element-name="improvement">
|
||||
<textarea rows="4" name="improvement" class="formbricks-textarea" required></textarea>
|
||||
<textarea rows="4" name="improvement" class="formbricks-textarea" required autofocus></textarea>
|
||||
<div class="formbricks-next-button-wrapper">
|
||||
<button type="submit" class="formbricks-next-button">Next</button>
|
||||
</div>
|
||||
@@ -93,7 +123,7 @@
|
||||
<div class="formbricks-element formbricks-hidden" id="formbricks-question-4">
|
||||
<p class="formbricks-element-label">What type of people would benefit most from using our service?</p>
|
||||
<form class="formbricks-form" data-element-name="selfSegmentation">
|
||||
<textarea rows="4" name="selfSegmentation" class="formbricks-textarea" required></textarea>
|
||||
<textarea rows="4" name="selfSegmentation" class="formbricks-textarea" required autofocus></textarea>
|
||||
<div class="formbricks-next-button-wrapper">
|
||||
<button type="submit" class="formbricks-next-button">Submit</button>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createFocusTrap } from "focus-trap";
|
||||
import { formHtml } from "./form-html";
|
||||
import formCss from "./form.css";
|
||||
|
||||
@@ -27,6 +28,11 @@ window.addEventListener("load", init);
|
||||
const formContainer = document.createElement("div");
|
||||
formContainer.id = "formbricks-container";
|
||||
|
||||
const trap = createFocusTrap(formContainer, {
|
||||
initialFocus: "#formbricksPmf-0-0",
|
||||
allowOutsideClick: true,
|
||||
});
|
||||
|
||||
function init() {
|
||||
// add css to head
|
||||
if (document.getElementById("formbricksPmf__css") === null) {
|
||||
@@ -49,9 +55,12 @@ function init() {
|
||||
// add listeners
|
||||
// radio buttons
|
||||
Array.from(
|
||||
formContainer.getElementsByClassName("formbricks-radio-option") as HTMLCollectionOf<HTMLElement>
|
||||
formContainer.getElementsByClassName("formbricks-radio-input") as HTMLCollectionOf<HTMLElement>
|
||||
).forEach((el) => {
|
||||
el.addEventListener("click", () => submitElement(el.dataset?.elementName, el.dataset?.elementValue));
|
||||
el.addEventListener("keypress", function (e) {
|
||||
if (e.key === "Enter") return submitElement(el.dataset?.elementName, el.dataset?.elementValue);
|
||||
});
|
||||
});
|
||||
// text inputs
|
||||
Array.from(
|
||||
@@ -63,6 +72,7 @@ function init() {
|
||||
submitElement(el.dataset?.elementName, e.target.elements[el.dataset?.elementName].value);
|
||||
};
|
||||
});
|
||||
trap.activate();
|
||||
sendWarmupRequest();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
window.formbricksPmf = {
|
||||
...window.formbricksPmf,
|
||||
config: {
|
||||
formbricksUrl: "https://app.formbricks.com",
|
||||
formId: "cldekteoj0000nr0gjlvp34ny",
|
||||
formbricksUrl: "http://localhost:3000",
|
||||
formId: "cldbru2nu000s19t6mtc4bhk4",
|
||||
containerId: "test-div",
|
||||
contact: {
|
||||
name: "Peer",
|
||||
@@ -31,4 +31,5 @@
|
||||
</head>
|
||||
<body style="background-color: #ccc">
|
||||
<div id="test-div" style="background-color: #fff; width: 40rem; height: 30rem"></div>
|
||||
<button>Test</button>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user