Skip to content

Commit

Permalink
fix space scrolling page selecting radio inputs and color inputs + im…
Browse files Browse the repository at this point in the history
…prove keyboard navigation in radio groups
  • Loading branch information
aalves08 committed Feb 24, 2025
1 parent d669a7c commit 644dd32
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 9 deletions.
20 changes: 17 additions & 3 deletions pkg/rancher-components/src/components/Form/Radio/RadioButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ export default defineComponent({
description: {
type: String,
default: null
},
/**
* Prevent focus when using radio in the context of a Radio group
*/
preventFocusOnRadioGroups: {
type: Boolean,
default: false
}
},
Expand Down Expand Up @@ -111,7 +119,7 @@ export default defineComponent({
watch: {
value(neu) {
this.isChecked = this.val === neu;
if (this.isChecked) {
if (this.isChecked && !this.preventFocusOnRadioGroups) {
(this.$refs.custom as HTMLElement).focus();
}
}
Expand All @@ -136,7 +144,11 @@ export default defineComponent({

<template>
<label
:class="{'disabled': isDisabled, 'radio-container': true}"
:class="{
'disabled': isDisabled,
'radio-container': true,
'radio-button-checked': isChecked
}"
@keydown.enter="clicked($event)"
@keydown.space="clicked($event)"
@click.stop="clicked($event)"
Expand All @@ -154,7 +166,7 @@ export default defineComponent({
<span
ref="custom"
:class="[ isDisabled ? 'text-muted' : '', 'radio-custom']"
:tabindex="isDisabled ? -1 : 0"
:tabindex="isDisabled || preventFocusOnRadioGroups ? -1 : 0"
:aria-label="label"
:aria-checked="isChecked"
role="radio"
Expand Down Expand Up @@ -220,9 +232,11 @@ $fontColor: var(--input-label);
display: inline-flex;
align-items: flex-start;
margin: 0;
left: -4px;
user-select: none;
border-radius: var(--border-radius);
padding-bottom: 5px;
padding-left: 4px;
&,
.radio-label,
Expand Down
45 changes: 39 additions & 6 deletions pkg/rancher-components/src/components/Form/Radio/RadioGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export default defineComponent({
emits: ['update:value'],
data() {
return { currFocusedElem: undefined as undefined | EventTarget | null };
},
computed: {
/**
* Creates a collection of Options from the provided props.
Expand Down Expand Up @@ -151,12 +155,33 @@ export default defineComponent({
}
},
beforeUnmount() {
const radioGroup = this.$refs?.radioGroup as HTMLInputElement;
radioGroup.removeEventListener('focusin', this.focusChanged);
},
mounted() {
const radioGroup = this.$refs?.radioGroup as HTMLInputElement;
radioGroup.addEventListener('focusin', this.focusChanged);
},
methods: {
focusChanged(ev: Event) {
this.currFocusedElem = ev.target;
},
/**
* Keyboard left/right event listener to select next/previous option. Emits
* the input event.
*/
clickNext(direction: number): void {
clickNext(ev: Event, direction: number): void {
// moving focus away from a custom group element and pressing arrow keys
// should not have any effect on the group - custom UI for radiogroup option(s)
if (this.currFocusedElem !== this.$refs?.radioGroup) {
return;
}
const opts = this.normalizedOptions;
const selected = opts.find((x) => x.value === this.value);
let newIndex = (selected ? opts.indexOf(selected) : -1) + direction;
Expand Down Expand Up @@ -205,12 +230,15 @@ export default defineComponent({

<!-- Group -->
<div
ref="radioGroup"
role="radiogroup"
:aria-label="radioGroupLabel"
class="radio-group"
:class="{'row':row}"
@keyup.down.stop="clickNext(1)"
@keyup.up.stop="clickNext(-1)"
tabindex="0"
@keydown.down.prevent.stop="clickNext($event, 1)"
@keydown.up.prevent.stop="clickNext($event, -1)"
@keydown.space.enter.stop.prevent
>
<div
v-for="(option, i) in normalizedOptions"
Expand All @@ -231,6 +259,7 @@ export default defineComponent({
:val="option.value"
:disabled="isDisabled"
:mode="mode"
:prevent-focus-on-radio-groups="true"
@update:value="$emit('update:value', $event)"
/>
</slot>
Expand All @@ -241,9 +270,13 @@ export default defineComponent({

<style lang='scss'>
.radio-group {
&:focus {
border:none;
outline:none;
&:focus, &:focus-visible {
border: none;
outline: none;
}
&:focus-visible .radio-button-checked {
@include focus-outline;
}
h3 {
Expand Down
1 change: 1 addition & 0 deletions shell/components/LandingPagePreference.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default {
:val="false"
:value="afterLoginRoute=== 'home' || afterLoginRoute === 'last-visited'"
:v-bind="$attrs"
:prevent-focus-on-radio-groups="true"
@update:value="afterLoginRoute = false"
/>
<Select
Expand Down
1 change: 1 addition & 0 deletions shell/components/form/ColorInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export default {
:class="{[mode]:mode, disabled: isDisabled}"
:data-testid="componentTestid + '-color-input'"
:tabindex="isDisabled ? -1 : 0"
@keydown.space.prevent
@keyup.enter.space.stop="handleKeyup($event)"
>
<label class="text-label"><t
Expand Down

0 comments on commit 644dd32

Please sign in to comment.