Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace element ids with custom attributes for Widget-annotations (issue 15056) #15057

Merged
merged 1 commit into from
Jun 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,9 @@ class AnnotationElement {
const exportValue =
typeof exportValues === "string" ? exportValues : null;

const domElement = document.getElementById(id);
const domElement = document.querySelector(
`[data-element-id="${id}"]`
);
if (domElement && !GetElementsByNameSet.has(domElement)) {
warn(`_getElementsByName - element not allowed: ${id}`);
continue;
Expand Down Expand Up @@ -570,7 +572,7 @@ class LinkAnnotationElement extends AnnotationElement {
render() {
const { data, linkService } = this;
const link = document.createElement("a");
link.setAttribute("id", data.id);
link.setAttribute("data-element-id", data.id);
let isBound = false;

if (data.url) {
Expand Down Expand Up @@ -775,8 +777,12 @@ class LinkAnnotationElement extends AnnotationElement {
default:
continue;
}
const domElement = document.getElementById(id);
if (!domElement || !GetElementsByNameSet.has(domElement)) {

const domElement = document.querySelector(`[data-element-id="${id}"]`);
if (!domElement) {
continue;
} else if (!GetElementsByNameSet.has(domElement)) {
warn(`_bindResetFormAction - element not allowed: ${id}`);
continue;
}
domElement.dispatchEvent(new Event("resetform"));
Expand Down Expand Up @@ -989,7 +995,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
});
const textContent = storedData.formattedValue || storedData.value || "";
const elementData = {
userValue: null,
userValue: textContent,
formattedValue: null,
valueOnFocus: "",
};
Expand All @@ -1009,13 +1015,12 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
}
}
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);

element.disabled = this.data.readOnly;
element.name = this.data.fieldName;
element.tabIndex = DEFAULT_TAB_INDEX;

elementData.userValue = textContent;
element.setAttribute("id", id);

this._setRequired(element, this.data.required);

element.addEventListener("input", event => {
Expand Down Expand Up @@ -1262,14 +1267,15 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {

const element = document.createElement("input");
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);

element.disabled = data.readOnly;
this._setRequired(element, this.data.required);
element.type = "checkbox";
element.name = data.fieldName;
if (value) {
element.setAttribute("checked", true);
}
element.setAttribute("id", id);
element.setAttribute("exportValue", data.exportValue);
element.tabIndex = DEFAULT_TAB_INDEX;

Expand Down Expand Up @@ -1346,14 +1352,15 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {

const element = document.createElement("input");
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);

element.disabled = data.readOnly;
this._setRequired(element, this.data.required);
element.type = "radio";
element.name = data.fieldName;
if (value) {
element.setAttribute("checked", true);
}
element.setAttribute("id", id);
element.tabIndex = DEFAULT_TAB_INDEX;

element.addEventListener("change", event => {
Expand Down Expand Up @@ -1463,10 +1470,11 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {

const selectElement = document.createElement("select");
GetElementsByNameSet.add(selectElement);
selectElement.setAttribute("data-element-id", id);

selectElement.disabled = this.data.readOnly;
this._setRequired(selectElement, this.data.required);
selectElement.name = this.data.fieldName;
selectElement.setAttribute("id", id);
selectElement.tabIndex = DEFAULT_TAB_INDEX;

let addAnEmptyEntry = this.data.combo && this.data.options.length > 0;
Expand Down
105 changes: 54 additions & 51 deletions test/integration/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
* limitations under the License.
*/

const { closePages, loadAndWait } = require("./test_utils.js");
const {
closePages,
getSelector,
getQuerySelector,
loadAndWait,
} = require("./test_utils.js");

describe("Annotation highlight", () => {
describe("annotation-highlight.pdf", () => {
Expand Down Expand Up @@ -108,18 +113,14 @@ describe("Text widget", () => {
const base = "hello world";
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.type("#\\32 5R", base);
await page.waitForFunction(
`document.querySelector("#\\\\32 4R").value !== ""`
);
await page.waitForFunction(
`document.querySelector("#\\\\32 6R").value !== ""`
);
await page.type(getSelector("25R"), base);
await page.waitForFunction(`${getQuerySelector("24R")}.value !== ""`);
await page.waitForFunction(`${getQuerySelector("26R")}.value !== ""`);

let text = await page.$eval("#\\32 4R", el => el.value);
let text = await page.$eval(getSelector("24R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual(base);

text = await page.$eval("#\\32 6R", el => el.value);
text = await page.$eval(getSelector("26R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual(base);
})
);
Expand All @@ -145,15 +146,15 @@ describe("Annotation and storage", () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
// Text field.
await page.type("#\\36 4R", text1);
await page.type(getSelector("64R"), text1);
// Checkbox.
await page.click("[data-annotation-id='65R']");
// Radio.
await page.click("[data-annotation-id='67R']");

for (const [pageNumber, textId, checkId, radio1Id, radio2Id] of [
[2, "#\\31 8R", "#\\31 9R", "#\\32 1R", "#\\32 0R"],
[5, "#\\32 3R", "#\\32 4R", "#\\32 2R", "#\\32 5R"],
[2, "18R", "19R", "21R", "20R"],
[5, "23R", "24R", "22R", "25R"],
]) {
await page.evaluate(n => {
window.document
Expand All @@ -162,34 +163,37 @@ describe("Annotation and storage", () => {
}, pageNumber);

// Need to wait to have a displayed text input.
await page.waitForSelector(textId, {
await page.waitForSelector(getSelector(textId), {
timeout: 0,
});

const text = await page.$eval(textId, el => el.value);
const text = await page.$eval(getSelector(textId), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual(text1);

let checked = await page.$eval(checkId, el => el.checked);
let checked = await page.$eval(
getSelector(checkId),
el => el.checked
);
expect(checked).toEqual(true);

checked = await page.$eval(radio1Id, el => el.checked);
checked = await page.$eval(getSelector(radio1Id), el => el.checked);
expect(checked).toEqual(false);

checked = await page.$eval(radio2Id, el => el.checked);
checked = await page.$eval(getSelector(radio2Id), el => el.checked);
expect(checked).toEqual(false);
}

// Change data on page 5 and check that other pages changed.
// Text field.
await page.type("#\\32 3R", text2);
await page.type(getSelector("23R"), text2);
// Checkbox.
await page.click("[data-annotation-id='24R']");
// Radio.
await page.click("[data-annotation-id='25R']");

for (const [pageNumber, textId, checkId, radio1Id, radio2Id] of [
[1, "#\\36 4R", "#\\36 5R", "#\\36 7R", "#\\36 8R"],
[2, "#\\31 8R", "#\\31 9R", "#\\32 1R", "#\\32 0R"],
[1, "64R", "65R", "67R", "68R"],
[2, "18R", "19R", "21R", "20R"],
]) {
await page.evaluate(n => {
window.document
Expand All @@ -198,22 +202,25 @@ describe("Annotation and storage", () => {
}, pageNumber);

// Need to wait to have a displayed text input.
await page.waitForSelector(textId, {
await page.waitForSelector(getSelector(textId), {
timeout: 0,
});

const text = await page.$eval(textId, el => el.value);
const text = await page.$eval(getSelector(textId), el => el.value);
expect(text)
.withContext(`In ${browserName}`)
.toEqual(text2 + text1);

let checked = await page.$eval(checkId, el => el.checked);
let checked = await page.$eval(
getSelector(checkId),
el => el.checked
);
expect(checked).toEqual(false);

checked = await page.$eval(radio1Id, el => el.checked);
checked = await page.$eval(getSelector(radio1Id), el => el.checked);
expect(checked).toEqual(false);

checked = await page.$eval(radio2Id, el => el.checked);
checked = await page.$eval(getSelector(radio2Id), el => el.checked);
expect(checked).toEqual(false);
}
})
Expand All @@ -238,8 +245,8 @@ describe("ResetForm action", () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const base = "hello world";
for (let i = 3; i <= 7; i++) {
await page.type(`#\\36 ${i}R`, base);
for (let i = 63; i <= 67; i++) {
await page.type(getSelector(`${i}R`), base);
}

const selectors = [69, 71, 75].map(
Expand All @@ -249,36 +256,34 @@ describe("ResetForm action", () => {
await page.click(selector);
}

await page.select("#\\37 8R", "b");
await page.select("#\\38 1R", "f");
await page.select(getSelector("78R"), "b");
await page.select(getSelector("81R"), "f");

await page.click("[data-annotation-id='82R']");
await page.waitForFunction(
`document.querySelector("#\\\\36 3R").value === ""`
);
await page.waitForFunction(`${getQuerySelector("63R")}.value === ""`);

for (let i = 3; i <= 8; i++) {
const text = await page.$eval(`#\\36 ${i}R`, el => el.value);
for (let i = 63; i <= 68; i++) {
const text = await page.$eval(getSelector(`${i}R`), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("");
}

const ids = [69, 71, 72, 73, 74, 75, 76, 77];
for (const id of ids) {
const checked = await page.$eval(
`#\\3${Math.floor(id / 10)} ${id % 10}R`,
getSelector(`${id}R`),
el => el.checked
);
expect(checked).withContext(`In ${browserName}`).toEqual(false);
}

let selected = await page.$eval(
`#\\37 8R [value="a"]`,
`${getSelector("78R")} [value="a"]`,
el => el.selected
);
expect(selected).withContext(`In ${browserName}`).toEqual(true);

selected = await page.$eval(
`#\\38 1R [value="d"]`,
`${getSelector("81R")} [value="d"]`,
el => el.selected
);
expect(selected).withContext(`In ${browserName}`).toEqual(true);
Expand All @@ -290,8 +295,8 @@ describe("ResetForm action", () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const base = "hello world";
for (let i = 3; i <= 8; i++) {
await page.type(`#\\36 ${i}R`, base);
for (let i = 63; i <= 68; i++) {
await page.type(getSelector(`${i}R`), base);
}

const selectors = [69, 71, 72, 73, 75].map(
Expand All @@ -301,24 +306,22 @@ describe("ResetForm action", () => {
await page.click(selector);
}

await page.select("#\\37 8R", "b");
await page.select("#\\38 1R", "f");
await page.select(getSelector("78R"), "b");
await page.select(getSelector("81R"), "f");

await page.click("[data-annotation-id='84R']");
await page.waitForFunction(
`document.querySelector("#\\\\36 3R").value === ""`
);
await page.waitForFunction(`${getQuerySelector("63R")}.value === ""`);

for (let i = 3; i <= 8; i++) {
for (let i = 63; i <= 68; i++) {
const expected = (i - 3) % 2 === 0 ? "" : base;
const text = await page.$eval(`#\\36 ${i}R`, el => el.value);
const text = await page.$eval(getSelector(`${i}R`), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual(expected);
}

let ids = [69, 72, 73, 74, 76, 77];
for (const id of ids) {
const checked = await page.$eval(
`#\\3${Math.floor(id / 10)} ${id % 10}R`,
getSelector(`${id}R`),
el => el.checked
);
expect(checked)
Expand All @@ -329,20 +332,20 @@ describe("ResetForm action", () => {
ids = [71, 75];
for (const id of ids) {
const checked = await page.$eval(
`#\\3${Math.floor(id / 10)} ${id % 10}R`,
getSelector(`${id}R`),
el => el.checked
);
expect(checked).withContext(`In ${browserName}`).toEqual(true);
}

let selected = await page.$eval(
`#\\37 8R [value="a"]`,
`${getSelector("78R")} [value="a"]`,
el => el.selected
);
expect(selected).withContext(`In ${browserName}`).toEqual(true);

selected = await page.$eval(
`#\\38 1R [value="f"]`,
`${getSelector("81R")} [value="f"]`,
el => el.selected
);
expect(selected).withContext(`In ${browserName}`).toEqual(true);
Expand Down
Loading