Skip to content

Commit f380b21

Browse files
committed
chore: cache aria helper, label to updated lifecycle
1 parent ba8e5c9 commit f380b21

File tree

5 files changed

+81
-16
lines changed

5 files changed

+81
-16
lines changed

packages/button/src/ButtonBase.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,7 @@ export class ButtonBase extends ObserveSlotText(LikeAnchor(Focusable), '', [
225225
if (!this.hasAttribute('tabindex')) {
226226
this.setAttribute('tabindex', '0');
227227
}
228-
if (changed.has('label')) {
229-
if (this.label) {
230-
this.setAttribute('aria-label', this.label);
231-
} else {
232-
this.removeAttribute('aria-label');
233-
}
234-
}
228+
235229
this.manageAnchor();
236230
this.addEventListener('keydown', this.handleKeydown);
237231
this.addEventListener('keypress', this.handleKeypress);
@@ -243,6 +237,14 @@ export class ButtonBase extends ObserveSlotText(LikeAnchor(Focusable), '', [
243237
this.manageAnchor();
244238
}
245239

240+
if (changed.has('label')) {
241+
if (this.label) {
242+
this.setAttribute('aria-label', this.label);
243+
} else {
244+
this.removeAttribute('aria-label');
245+
}
246+
}
247+
246248
if (this.anchorElement) {
247249
// Ensure the anchor element is not focusable directly via tab
248250
this.anchorElement.tabIndex = -1;

packages/button/stories/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12+
/* eslint-disable import/no-extraneous-dependencies */
1213
import { html, TemplateResult } from '@spectrum-web-components/base';
1314
import { ifDefined } from '@spectrum-web-components/base/src/directives.js';
1415

@@ -86,6 +87,17 @@ export const argTypes = {
8687
type: 'boolean',
8788
},
8889
},
90+
label: {
91+
name: 'label',
92+
type: { name: 'string', required: false },
93+
description: 'The label to apply to the aria-label of the button.',
94+
table: {
95+
type: { summary: 'string' },
96+
},
97+
control: {
98+
type: 'text',
99+
},
100+
},
89101
};
90102

91103
export const makeOverBackground =
@@ -122,6 +134,7 @@ export function renderButton(properties: Properties): TemplateResult {
122134
treatment=${ifDefined(properties.treatment)}
123135
variant=${ifDefined(properties.variant)}
124136
static-color=${ifDefined(properties.staticColor)}
137+
label=${ifDefined(properties.label)}
125138
>
126139
${properties.content || 'Click Me'}
127140
</sp-button>

packages/button/stories/template.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
12+
/* eslint-disable import/no-extraneous-dependencies */
1313
import { html, TemplateResult } from '@spectrum-web-components/base';
1414
import { ifDefined } from '@spectrum-web-components/base/src/directives.js';
1515
import {
@@ -30,6 +30,7 @@ export interface Properties {
3030
target?: '_blank' | '_parent' | '_self' | '_top';
3131
noWrap?: boolean;
3232
iconOnly?: boolean;
33+
label?: string;
3334
}
3435

3536
export const Template = ({

packages/button/test/button.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,45 @@ describe('Button', () => {
423423
expect(el.getAttribute('aria-label')).to.equal('clickable');
424424
});
425425

426+
it('updates aria-label when label changes', async () => {
427+
const el = await fixture<Button>(html`
428+
<sp-button label="Initial label">Button</sp-button>
429+
`);
430+
431+
await elementUpdated(el);
432+
expect(el.getAttribute('aria-label')).to.equal('Initial label');
433+
434+
// Change the label
435+
el.label = 'New Label';
436+
await elementUpdated(el);
437+
438+
// The aria-label should also update
439+
expect(el.getAttribute('aria-label')).to.equal('New Label');
440+
});
441+
442+
it('preserves aria-label when slot content changes', async () => {
443+
const el = await fixture<Button>(html`
444+
<sp-button label="Test label">Initial Content</sp-button>
445+
`);
446+
447+
await elementUpdated(el);
448+
expect(el.getAttribute('aria-label')).to.equal('Test label');
449+
450+
// Change the slot content
451+
el.textContent = 'Updated content';
452+
await elementUpdated(el);
453+
454+
// The aria-label should still be preserved
455+
expect(el.getAttribute('aria-label')).to.equal('Test label');
456+
457+
// Change slot content again
458+
el.innerHTML = '<span>New content</span>';
459+
await elementUpdated(el);
460+
461+
// The aria-label should still be preserved
462+
expect(el.getAttribute('aria-label')).to.equal('Test label');
463+
});
464+
426465
it('manages aria-label set from outside', async () => {
427466
const el = await fixture<Button>(html`
428467
<sp-button

tools/reactive-controllers/src/PendingState.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import { html, LitElement, ReactiveController, TemplateResult } from 'lit';
1413
import '@spectrum-web-components/progress-circle/sp-progress-circle.js';
14+
import { html, LitElement, ReactiveController, TemplateResult } from 'lit';
1515

1616
/**
1717
* Represents a host element with pending state.
@@ -58,7 +58,7 @@ export class PendingStateController<T extends HostWithPendingState>
5858
id="loader"
5959
size="s"
6060
indeterminate
61-
aria-valuetext=${pendingLabel}
61+
aria-label=${pendingLabel}
6262
class="progress-circle"
6363
></sp-progress-circle>
6464
`
@@ -73,15 +73,25 @@ export class PendingStateController<T extends HostWithPendingState>
7373
const { pending, disabled, pendingLabel } = this.host;
7474
const currentAriaLabel = this.host.getAttribute('aria-label');
7575

76+
function shouldCacheAriaLabel(
77+
cached: string | null,
78+
current: string | null,
79+
pending: string | undefined
80+
): string | boolean | null {
81+
return (
82+
(!cached && current && current !== pending) ||
83+
(cached !== current && current && current !== pending)
84+
);
85+
}
86+
7687
// If the current `aria-label` is different from the pending label, cache it
7788
// or if the cached `aria-label` is different from the current `aria-label`, cache it
7889
if (
79-
(!this.cachedAriaLabel &&
80-
currentAriaLabel &&
81-
currentAriaLabel !== pendingLabel) ||
82-
(this.cachedAriaLabel !== currentAriaLabel &&
83-
currentAriaLabel &&
84-
currentAriaLabel !== pendingLabel)
90+
shouldCacheAriaLabel(
91+
this.cachedAriaLabel,
92+
currentAriaLabel,
93+
pendingLabel
94+
)
8595
) {
8696
this.cachedAriaLabel = currentAriaLabel;
8797
}

0 commit comments

Comments
 (0)