Skip to content

Commit b139482

Browse files
Pavitra KhatriPavitra Khatri
Pavitra Khatri
authored and
Pavitra Khatri
committed
Focus on first visible field in form and in navigable layouts
1 parent 80e41c7 commit b139482

File tree

8 files changed

+128
-5
lines changed

8 files changed

+128
-5
lines changed

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/commons/js/common.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
}
8888
}
8989

90-
90+
9191
/**
9292
* Returns all expanded items.
9393
*
@@ -225,6 +225,22 @@
225225
panel.classList.add(this.constructor.cssClasses.panel.expanded);
226226
panel.classList.remove(this.constructor.cssClasses.panel.hidden);
227227
panel.setAttribute("aria-hidden", false);
228+
229+
// To set the focus on the field when tab expands
230+
if (document.activeElement === button) {
231+
setTimeout(() => {
232+
let id = panel.id.replace(this.constructor.idSuffixes.panel, "");
233+
const form = this.formContainer.getModel();
234+
const tab = this.getModel()._jsonModel.items.find(item => item.id === id);
235+
236+
if (tab && Array.isArray(tab.items) && tab.items.length > 0) {
237+
const field = form.getElement(tab.id);
238+
if (field) {
239+
form.setFocus(field);
240+
}
241+
}
242+
}, 0)
243+
}
228244
}
229245
}
230246

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/accordion/v1/accordion/clientlibs/site/js/accordionview.js

-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@
9797
this.expandItem(item)
9898
}
9999

100-
101100
#collapseAllItems() {
102101
var items = this.getCachedItems();
103102
if (items) {

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/wizard/v1/wizard/clientlibs/site/js/wizardview.js

+8
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@
199199
}
200200

201201

202+
getFocusElementId() {
203+
const activeIndex = this._active;
204+
const activeTabElement = this.getCachedTabs()[activeIndex];
205+
return activeTabElement.id.substring(0, activeTabElement.id.lastIndexOf(Wizard.#tabIdSuffix));
206+
}
207+
202208
#navigateToNextTab() {
203209
const activeIndex = this._active;
204210
const activeTabElement = this.getCachedTabs()[activeIndex];
@@ -334,6 +340,8 @@
334340
#navigateAndFocusTab(index) {
335341
this.navigate(index);
336342
this.focusWithoutScroll(this.getCachedTabs()[index]);
343+
const id = this.getFocusElementId();
344+
this.focusToFirstVisibleField(id);
337345
}
338346

339347
#syncWizardNavLabels() {

ui.frontend/src/view/FormPanel.js

+45
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,51 @@ class FormPanel extends FormFieldBase {
7575
this.parentView.setFocus(this.getId());
7676
}
7777
}
78+
79+
/**
80+
* Sets the focus to first field while navigating in navigable layouts.
81+
* @param {string} id - The ID of the field to set focus to.
82+
*/
83+
focusToFirstVisibleField(id) {
84+
const form = this.formContainer.getModel();
85+
const activeTab = this.getModel()._jsonModel.items.find(item => item.id === id);
86+
87+
if (!activeTab) {
88+
return;
89+
}
90+
91+
const findFirstPanelNonPanelField = (item) => {
92+
// if item is not a panel, return it
93+
if (item.fieldType && item.fieldType !== "panel") {
94+
const field = form.getElement(item.id);
95+
if (field && field._jsonModel.visible) {
96+
return field;
97+
}
98+
return null;
99+
}
100+
101+
// If it's a panel, check its items
102+
if (item[':items']) {
103+
const itemsToCheck = item[':itemsOrder']
104+
? item[':itemsOrder'].map(key => item[':items'][key])
105+
: Object.values(item[':items']);
106+
107+
// Check each child
108+
for (const childItem of itemsToCheck) {
109+
const result = findFirstPanelNonPanelField(childItem);
110+
if (result) {
111+
return result;
112+
}
113+
}
114+
}
115+
return null;
116+
};
117+
118+
const firstField = findFirstPanelNonPanelField(activeTab);
119+
if (firstField) {
120+
form.setFocus(firstField);
121+
}
122+
}
78123

79124
/**
80125
* Adds a child view to the FormPanel.

ui.frontend/src/view/FormTabs.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,12 @@ class FormTabs extends FormPanel {
249249
if (this.#_active !== tabId) {
250250
this.navigate(tabId);
251251
this.focusWithoutScroll(this.#getTabNavElementById(tabId));
252+
const id = this.getActiveTabId(this.#getCachedTabs()).replace(this.#tabIdSuffix,"");
253+
this.focusToFirstVisibleField(id);
252254
}
253255
}
254256

255257

256-
257258
#getTabNavElementById(tabId) {
258259
var tabs = this.#getCachedTabs();
259260
if (tabs) {
@@ -328,7 +329,6 @@ class FormTabs extends FormPanel {
328329
}
329330
}
330331

331-
332332
/**
333333
* Synchronizes tab labels with their corresponding tab panels.
334334
* Updates the ID and aria-controls attribute of each tab label.

ui.tests/test-module/specs/accordion/accordion.runtime.cy.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ describe("Form with Accordion Layout Container with focus", () => {
197197
cy.get(`#${secondChildComponentButtonId}`).should('have.class', 'cmp-accordion__button--expanded');
198198
cy.get(`#${firstChildComponentButtonId}`).should('not.have.class', 'cmp-accordion__button--expanded');
199199
cy.get(`#${firstChildComponentPanelId}`).should('not.have.class', 'cmp-accordion__panel--expanded');
200-
201200
cy.get(`#${firstChildComponentButtonId}`).then(() => {
202201
formContainer.setFocus(id);
203202
cy.get(`#${firstChildComponentButtonId}`).isElementInViewport().should("eq", true);
@@ -212,6 +211,27 @@ describe("Form with Accordion Layout Container with focus", () => {
212211
});
213212
});
214213
});
214+
215+
it("on clicking of expand button, focus should be visible on first component in the current tab ", () => {
216+
const [id, fieldView] = Object.entries(formContainer._fields)[0];
217+
const firstChildComponentId = formContainer._model.items[0].items[0].id;
218+
const firstChildComponentButtonId = firstChildComponentId + "-button";
219+
const firstChildComponentPanelId = firstChildComponentId + "-panel";
220+
221+
const secondChildComponentId = formContainer._model.items[0].items[1].id;
222+
const secondChildComponentButtonId = secondChildComponentId + "-button";
223+
const secondChildComponentPanelId = secondChildComponentId + "-panel";
224+
225+
cy.get(`#${secondChildComponentButtonId}`).click({force: true}).then(() => {
226+
cy.get('input[name="textinputfa2"]').should('be.focused');
227+
cy.get(`#${secondChildComponentPanelId}`).should('have.class', 'cmp-accordion__panel--expanded');
228+
})
229+
230+
cy.get(`#${firstChildComponentButtonId}`).click({force: true}).then(() => {
231+
cy.get('input[name="textinputfa1"]').should('be.focused');
232+
cy.get(`#${firstChildComponentPanelId}`).should('have.class', 'cmp-accordion__panel--expanded');
233+
})
234+
})
215235
});
216236

217237
describe("Form with Accordion Layout Container with Hidden Children", () => {

ui.tests/test-module/specs/tabsontop/tabsontop.runtime.cy.js

+16
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ describe("Form with Tabsontop Layout Container with focus", () => {
167167

168168
});
169169

170+
it("focus should be on the first component when navigated to the next tab", () => {
171+
const [id, fieldView] = Object.entries(formContainer._fields)[0];
172+
tab2().click().then(() => {
173+
tab2().should('have.class', 'cmp-tabs__tab--active');
174+
tab2().should('have.attr', 'aria-selected', 'true');
175+
cy.get('input[name="textinputft2"]').should('be.focused');
176+
tab1().should('have.attr', 'aria-selected', 'false');
177+
});
178+
tab1().click().then(() => {
179+
tab1().should('have.class', 'cmp-tabs__tab--active');
180+
tab1().should('have.attr', 'aria-selected', 'true');
181+
cy.get('input[name="textinputft1"]').should('be.focused');
182+
tab2().should('have.attr', 'aria-selected', 'false');
183+
});
184+
})
185+
170186

171187
});
172188
describe("Form with TabsOnTop Layout Container with Hidden Children", () => {

ui.tests/test-module/specs/wizard/wizard.runtime.cy.js

+19
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,25 @@ describe("Form with Wizard Layout Container with focus", () => {
273273
});
274274
});
275275

276+
it("first component should always have focus when navigated", () => {
277+
const [id, fieldView] = Object.entries(formContainer._fields)[0];
278+
279+
cy.get(".cmp-adaptiveform-wizard__nav--next").click({force: true}).then(() => {
280+
cy.get(".cmp-adaptiveform-wizard__tab").eq(0).should('not.have.class', 'cmp-adaptiveform-wizard__tab--active');
281+
cy.get(".cmp-adaptiveform-wizard__tab").eq(1).should('have.class', 'cmp-adaptiveform-wizard__tab--active');
282+
cy.get(".cmp-adaptiveform-wizard__wizardpanel").eq(0).should('not.have.class', 'cmp-adaptiveform-wizard__wizardpanel--active');
283+
cy.get(".cmp-adaptiveform-wizard__wizardpanel").eq(1).should('have.class', 'cmp-adaptiveform-wizard__wizardpanel--active');
284+
cy.get('input[name="textinputfw2"]').should('be.focused');
285+
});
286+
cy.get(".cmp-adaptiveform-wizard__nav--previous").click({force: true}).then(() => {
287+
cy.get(".cmp-adaptiveform-wizard__tab").eq(0).should('have.class', 'cmp-adaptiveform-wizard__tab--active');
288+
cy.get(".cmp-adaptiveform-wizard__tab").eq(1).should('not.have.class', 'cmp-adaptiveform-wizard__tab--active');
289+
cy.get(".cmp-adaptiveform-wizard__wizardpanel").eq(0).should('have.class', 'cmp-adaptiveform-wizard__wizardpanel--active');
290+
cy.get(".cmp-adaptiveform-wizard__wizardpanel").eq(1).should('not.have.class', 'cmp-adaptiveform-wizard__wizardpanel--active');
291+
cy.get('input[name="textinputfw1"]').should('be.focused');
292+
});
293+
})
294+
276295
});
277296

278297
describe("Form with wizard Layout Container with Hidden Children", () => {

0 commit comments

Comments
 (0)