diff --git a/src/components/radio-button/radio-button-group/radio-button-group.ts b/src/components/radio-button/radio-button-group/radio-button-group.ts
index b90c98bce0..5b6e041c19 100644
--- a/src/components/radio-button/radio-button-group/radio-button-group.ts
+++ b/src/components/radio-button/radio-button-group/radio-button-group.ts
@@ -2,7 +2,7 @@ import { CSSResultGroup, html, LitElement, nothing, PropertyValues, TemplateResu
import { customElement, property, state } from 'lit/decorators.js';
import { isArrowKeyPressed, getNextElementIndex, interactivityChecker } from '../../core/a11y';
-import { toggleDatasetEntry, setAttribute } from '../../core/dom';
+import { toggleDatasetEntry, setAttribute, isValidAttribute } from '../../core/dom';
import {
createNamedSlotState,
HandlerRepository,
@@ -241,7 +241,12 @@ export class SbbRadioButtonGroupElement extends LitElement {
}
private _getRadioTabIndex(radio: SbbRadioButtonElement): number {
- return (radio.checked || this._hasSelectionPanel) && !radio.disabled && !this.disabled ? 0 : -1;
+ const isSelected: boolean = radio.checked && !radio.disabled && !this.disabled;
+ const isParentPanelWithContent: boolean =
+ radio.parentElement.nodeName === 'SBB-SELECTION-PANEL' &&
+ isValidAttribute(radio.parentElement, 'data-has-content');
+
+ return isSelected || (this._hasSelectionPanel && isParentPanelWithContent) ? 0 : -1;
}
private _handleKeyDown(evt: KeyboardEvent): void {
@@ -262,17 +267,14 @@ export class SbbRadioButtonGroupElement extends LitElement {
return;
}
- let current: number;
- let nextIndex: number;
-
- if (this._hasSelectionPanel) {
- current = enabledRadios.findIndex((e: SbbRadioButtonElement) => e === evt.target);
- nextIndex = getNextElementIndex(evt, current, enabledRadios.length);
- } else {
- const checked: number = enabledRadios.findIndex(
- (radio: SbbRadioButtonElement) => radio.checked,
- );
- nextIndex = getNextElementIndex(evt, checked, enabledRadios.length);
+ const current: number = enabledRadios.findIndex((e: SbbRadioButtonElement) => e === evt.target);
+ const nextIndex: number = getNextElementIndex(evt, current, enabledRadios.length);
+
+ // Selection on arrow keypress is allowed only if all the selection-panels have no content.
+ const allPanelsHaveNoContent: boolean = (
+ Array.from(this.querySelectorAll?.('sbb-selection-panel')) || []
+ ).every((e) => !isValidAttribute(e, 'data-has-content'));
+ if (!this._hasSelectionPanel || (this._hasSelectionPanel && allPanelsHaveNoContent)) {
enabledRadios[nextIndex].select();
}
diff --git a/src/components/selection-panel/selection-panel.e2e.ts b/src/components/selection-panel/selection-panel.e2e.ts
index 381582680c..8ff7e00370 100644
--- a/src/components/selection-panel/selection-panel.e2e.ts
+++ b/src/components/selection-panel/selection-panel.e2e.ts
@@ -231,6 +231,64 @@ describe('sbb-selection-panel', () => {
});
});
+ describe('with radio group with no slotted content', () => {
+ it('focus selected, the focus and select on keyboard navigation', async () => {
+ await fixture(html`
+
+
+ Value one
+
+
+ Value two
+
+
+ Value three
+
+
+ Value four
+
+
+ `);
+ const wrapperNoContent = document.querySelector('#group-no-content');
+ const firstInputNoContent: SbbRadioButtonElement =
+ document.querySelector('#input-no-content-1');
+ const secondInputNoContent: SbbRadioButtonElement =
+ document.querySelector('#input-no-content-2');
+ const fourthInputNoContent: SbbRadioButtonElement =
+ document.querySelector('#input-no-content-4');
+
+ await sendKeys({ down: 'Tab' });
+ await waitForLitRender(wrapperNoContent);
+ expect(document.activeElement.id).to.be.equal(secondInputNoContent.id);
+
+ await sendKeys({ down: 'ArrowUp' });
+ await waitForLitRender(wrapperNoContent);
+ expect(document.activeElement.id).to.be.equal(firstInputNoContent.id);
+ expect(secondInputNoContent).not.to.have.attribute('checked');
+ expect(firstInputNoContent).to.have.attribute('checked');
+
+ await sendKeys({ down: 'ArrowRight' });
+ await waitForLitRender(wrapperNoContent);
+ expect(document.activeElement.id).to.be.equal(secondInputNoContent.id);
+ expect(firstInputNoContent).not.to.have.attribute('checked');
+ expect(secondInputNoContent).to.have.attribute('checked');
+
+ await sendKeys({ down: 'ArrowDown' });
+ await waitForLitRender(wrapperNoContent);
+ expect(document.activeElement.id).to.be.equal(fourthInputNoContent.id);
+ expect(secondInputNoContent).not.to.have.attribute('checked');
+ expect(fourthInputNoContent).to.have.attribute('checked');
+
+ await sendKeys({ down: 'ArrowLeft' });
+ await waitForLitRender(wrapperNoContent);
+ expect(document.activeElement.id).to.be.equal(secondInputNoContent.id);
+ expect(fourthInputNoContent).not.to.have.attribute('checked');
+ expect(secondInputNoContent).to.have.attribute('checked');
+ });
+ });
+
describe('with nested radio buttons', () => {
let nestedElement: SbbRadioButtonGroupElement;
diff --git a/src/components/selection-panel/selection-panel.stories.ts b/src/components/selection-panel/selection-panel.stories.ts
index fb0e370572..d9217676dc 100644
--- a/src/components/selection-panel/selection-panel.stories.ts
+++ b/src/components/selection-panel/selection-panel.stories.ts
@@ -472,6 +472,31 @@ const WithNoContentTemplate = ({
`;
+const WithNoContentGroupTemplate = ({
+ checkedInput,
+ disabledInput,
+ ...args
+}: Args): TemplateResult => html`
+
+
+ ${cardBadge()}
+
+ Value one ${suffixAndSubtext()}
+
+
+
+ ${cardBadge()}
+
+ Value two ${suffixAndSubtext()}
+
+
+
+ ${cardBadge()}
+ Value three ${suffixAndSubtext()}
+
+
+`;
+
export const WithCheckbox: StoryObj = {
render: WithCheckboxTemplate,
argTypes: basicArgTypes,
@@ -665,6 +690,12 @@ export const WithNoContentCheckedDisabled: StoryObj = {
args: { ...basicArgs, checkedInput: true, disabledInput: true },
};
+export const WithNoContentGroup: StoryObj = {
+ render: WithNoContentGroupTemplate,
+ argTypes: basicArgTypes,
+ args: { ...basicArgs, checkedInput: true },
+};
+
export const TicketsOptionsExample: StoryObj = {
render: TicketsOptionsExampleTemplate,
argTypes: basicArgTypes,