Skip to content

Commit

Permalink
🐛 fix(bal-file-upload): File upload component sets focus on different…
Browse files Browse the repository at this point in the history
… component on click (#1299)

* Create PR for #1283

* fix(field): links A11y information only for direct controls, labels and messages

* chore: remove console.log

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gery Hirschfeld <[email protected]>
  • Loading branch information
github-actions[bot] and hirsch88 authored Jan 23, 2024
1 parent 5b133aa commit e619596
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-roses-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@baloise/design-system-components': patch
---

Field component links A11y information only for direct controls, labels and messages.
73 changes: 54 additions & 19 deletions packages/components/src/components/bal-field/bal-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,28 +113,63 @@ export class Field implements ComponentInterface, BalMutationObserver {
await this.syncAriaAttributes()
}

private isDirectChild = (el: HTMLElement): boolean => {
if (!el) {
return false
}

const parent = el.parentElement
if (!parent) {
return false
}
if (parent.nodeName.toLowerCase() === 'bal-field' && parent !== this.el) {
return false
}
if (parent === this.el) {
return true
}
return this.isDirectChild(parent)
}

private findDirectChild = (selectors: string): BalAriaFormLinking | undefined => {
const element = this.el.querySelector<any>(selectors)
const isDirectChild = this.isDirectChild(element)
if (isDirectChild) {
return element
}
return undefined
}

private findDirectChildren = (selectors: string[]): BalAriaFormLinking[] => {
return selectors
.map(selector => {
return Array.from(this.el.querySelectorAll<any>(selector)).filter(this.isDirectChild)
})
.flat()
}

async syncAriaAttributes(): Promise<void> {
await deepReady(this.el)
await waitAfterFramePaint()

const label: BalAriaFormLinking = this.el.querySelector<any>('bal-field-label bal-label')
const message: BalAriaFormLinking = this.el.querySelector<any>('bal-field-message')
const controls: BalAriaFormLinking[] = [
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-input')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-select')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-input-date')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-checkbox')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-radio')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-checkbox-group')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-radio-group')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-number-input')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-time-input')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-datepicker')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-input-slider')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-input-stepper')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-textarea')),
...Array.from(this.el.querySelectorAll<any>('bal-field-control bal-file-upload')),
]
const label = this.findDirectChild('bal-field-label bal-label')
const message = this.findDirectChild('bal-field-message')
const controls = this.findDirectChildren([
'bal-field-control bal-input',
'bal-field-control bal-select',
'bal-field-control bal-input-date',
'bal-field-control bal-checkbox',
'bal-field-control bal-radio',
'bal-field-control bal-checkbox-group',
'bal-field-control bal-radio-group',
'bal-field-control bal-number-input',
'bal-field-control bal-time-input',
'bal-field-control bal-datepicker',
'bal-field-control bal-input-slider',
'bal-field-control bal-input-stepper',
'bal-field-control bal-textarea',
'bal-field-control bal-file-upload',
])

const ariaForm = defaultBalAriaForm

Expand Down Expand Up @@ -164,7 +199,7 @@ export class Field implements ComponentInterface, BalMutationObserver {

mutationObserverActive = true

@ListenToMutation({ subtree: false })
@ListenToMutation({ subtree: false, waitAfterFramePrint: true })
mutationListener(): void {
this.triggerAllHandlers()
this.syncAriaAttributes()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" href="/assets/baloise-design-system.css" />
<script type="module" src="/build/design-system-components.esm.js"></script>
<script nomodule src="/build/design-system-components.js"></script>
</head>
<body>
<bal-app>
<div class="container">
<bal-field>
<bal-field-control>
<bal-checkbox class="one">One</bal-checkbox>
</bal-field-control>

<bal-field>
<div>
<bal-field-control>
<bal-checkbox class="two">Two</bal-checkbox>
</bal-field-control>
</div>
</bal-field>
</bal-field>
</div>
</bal-app>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface MutationObserverOptions extends MutationObserverInit {
tags: string[]
closest?: string
waitAfterFramePrint?: boolean
}

export interface BalMutationObserver {
Expand Down
9 changes: 8 additions & 1 deletion packages/components/src/utils/mutation/mutation.listener.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { deepReady, waitAfterFramePaint } from '../helpers'
import { ListenerAbstract } from '../types/listener'
import { MutationObserverOptions } from './mutation.interfaces'

export class BalMutationListener extends ListenerAbstract {
private tags: string[] = []
private waitAfterFramePrint = false
private mutationObserver: MutationObserver | undefined = undefined
private mutationObserverInit: MutationObserverInit = {
childList: true,
Expand All @@ -13,6 +15,7 @@ export class BalMutationListener extends ListenerAbstract {

constructor(options: Partial<MutationObserverOptions>) {
super()
this.waitAfterFramePrint = options.waitAfterFramePrint || this.waitAfterFramePrint
this.tags = (options.tags || []).map(t => t.toUpperCase())
this.mutationObserverInit = {
childList: options.childList === false ? false : true,
Expand All @@ -22,11 +25,15 @@ export class BalMutationListener extends ListenerAbstract {
}
}

connect(el: HTMLElement): void {
async connect(el: HTMLElement) {
super.connect(el)
if (typeof MutationObserver === 'undefined') {
return
}
if (this.waitAfterFramePrint) {
await deepReady(el)
await waitAfterFramePaint()
}
this.destroyMutationObserver()
this.mutationObserver = new MutationObserver(this.mutationCallback)
this.mutationObserver.observe(el, this.mutationObserverInit)
Expand Down

0 comments on commit e619596

Please sign in to comment.