Skip to content

Commit 0056e6e

Browse files
committed
refactor(multiple): simplify LabelControl and include in public api
1 parent a2519f7 commit 0056e6e

6 files changed

Lines changed: 58 additions & 40 deletions

File tree

goldens/aria/private/index.api.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,22 @@ export interface HasElement {
429429
element: HTMLElement;
430430
}
431431

432+
// @public
433+
export class LabelControl {
434+
constructor(inputs: LabelControlInputs);
435+
// (undocumented)
436+
readonly inputs: LabelControlInputs;
437+
readonly label: SignalLike<string | undefined>;
438+
readonly labelledBy: SignalLike<string | undefined>;
439+
}
440+
441+
// @public
442+
export interface LabelControlInputs {
443+
defaultLabelledBy: SignalLike<string | undefined>;
444+
label: SignalLike<string | undefined>;
445+
labelledBy: SignalLike<string[] | undefined>;
446+
}
447+
432448
// @public (undocumented)
433449
export function linkedSignal<T>(sourceFn: () => T): WritableSignalLike<T>;
434450

src/aria/private/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ts_project(
1818
deps = [
1919
"//:node_modules/@angular/core",
2020
"//src/aria/private/accordion",
21+
"//src/aria/private/behaviors/label",
2122
"//src/aria/private/behaviors/signal-like",
2223
"//src/aria/private/combobox",
2324
"//src/aria/private/deferred-content",

src/aria/private/behaviors/label/label.spec.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,19 @@
77
*/
88

99
import {signal, WritableSignalLike} from '../signal-like/signal-like';
10-
import {LabelControl, LabelControlInputs, LabelControlOptionalInputs} from './label';
10+
import {LabelControl, LabelControlInputs} from './label';
1111

1212
// This is a helper type for the initial values passed to the setup function.
1313
type TestInputs = Partial<{
1414
label: string | undefined;
15-
defaultLabelledBy: string[];
15+
defaultLabelledBy: string;
1616
labelledBy: string[];
1717
}>;
1818

19-
type TestLabelControlInputs = LabelControlInputs & Required<LabelControlOptionalInputs>;
20-
2119
// This is a helper type to make all properties of LabelControlInputs writable signals.
2220
type WritableLabelControlInputs = {
23-
[K in keyof TestLabelControlInputs]: WritableSignalLike<
24-
TestLabelControlInputs[K] extends {(): infer T} ? T : never
21+
[K in keyof LabelControlInputs]: WritableSignalLike<
22+
LabelControlInputs[K] extends {(): infer T} ? T : never
2523
>;
2624
};
2725

@@ -30,7 +28,7 @@ function getLabelControl(initialValues: TestInputs = {}): {
3028
inputs: WritableLabelControlInputs;
3129
} {
3230
const inputs: WritableLabelControlInputs = {
33-
defaultLabelledBy: signal(initialValues.defaultLabelledBy ?? []),
31+
defaultLabelledBy: signal(initialValues.defaultLabelledBy),
3432
label: signal(initialValues.label),
3533
labelledBy: signal(initialValues.labelledBy ?? []),
3634
};
@@ -47,6 +45,11 @@ describe('LabelControl', () => {
4745
expect(control.label()).toBe('My Label');
4846
});
4947

48+
it('should return the user-provided label even ifdefaultLabelledBy is provided ', () => {
49+
const {control} = getLabelControl({defaultLabelledBy: 'default-id', label: 'My Label'});
50+
expect(control.label()).toBe('My Label');
51+
});
52+
5053
it('should return undefined if no label is provided', () => {
5154
const {control} = getLabelControl();
5255
expect(control.label()).toBeUndefined();
@@ -62,27 +65,27 @@ describe('LabelControl', () => {
6265
});
6366

6467
describe('#labelledBy', () => {
65-
it('should return user-provided labelledBy even if a label is provided', () => {
68+
it('should return user-provided labelledBy ids even if a label is provided', () => {
6669
const {control} = getLabelControl({
6770
label: 'My Label',
68-
defaultLabelledBy: ['default-id'],
69-
labelledBy: ['user-id'],
71+
defaultLabelledBy: 'default-id',
72+
labelledBy: ['user-id', 'user-id-2'],
7073
});
71-
expect(control.labelledBy()).toEqual(['user-id']);
74+
expect(control.labelledBy()).toEqual('user-id user-id-2');
7275
});
7376

7477
it('should return defaultLabelledBy if no user-provided labelledBy exists', () => {
75-
const {control} = getLabelControl({defaultLabelledBy: ['default-id']});
76-
expect(control.labelledBy()).toEqual(['default-id']);
78+
const {control} = getLabelControl({defaultLabelledBy: 'default-id'});
79+
expect(control.labelledBy()).toEqual('default-id');
7780
});
7881

7982
it('should update when label changes from undefined to a string', () => {
8083
const {control, inputs} = getLabelControl({
81-
defaultLabelledBy: ['default-id'],
84+
defaultLabelledBy: 'default-id',
8285
});
83-
expect(control.labelledBy()).toEqual(['default-id']);
86+
expect(control.labelledBy()).toEqual('default-id');
8487
inputs.label.set('A wild label appears');
85-
expect(control.labelledBy()).toEqual([]);
88+
expect(control.labelledBy()).toEqual(undefined);
8689
});
8790
});
8891
});

src/aria/private/behaviors/label/label.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,42 @@
77
*/
88
import {computed, SignalLike} from '../signal-like/signal-like';
99

10-
/** Represents the required inputs for the label control. */
10+
/** The required inputs for the label control. */
1111
export interface LabelControlInputs {
12-
/** The default `aria-labelledby` ids. */
13-
defaultLabelledBy: SignalLike<string[]>;
14-
}
12+
/** The default `aria-labelledby` id to use if no other inputs specified. */
13+
defaultLabelledBy: SignalLike<string | undefined>;
1514

16-
/** Represents the optional inputs for the label control. */
17-
export interface LabelControlOptionalInputs {
18-
/** The `aria-label`. */
19-
label?: SignalLike<string | undefined>;
15+
/** The `aria-label` to use instead of the default id. */
16+
label: SignalLike<string | undefined>;
2017

21-
/** The user-provided `aria-labelledby` ids. */
22-
labelledBy?: SignalLike<string[]>;
18+
/** The `aria-labelledby` id(s) to use instead of the default id (or label). */
19+
labelledBy: SignalLike<string[] | undefined>;
2320
}
2421

25-
/** Controls label and description of an element. */
22+
/** Controls label for an element. */
2623
export class LabelControl {
27-
/** The `aria-label`. */
28-
readonly label = computed(() => this.inputs.label?.());
24+
/** Use this value to set the `aria-label` attribute on the element. */
25+
readonly label = computed(() => this.inputs.label());
2926

30-
/** The `aria-labelledby` ids. */
27+
/** Use this value to set the `aria-labelledby` attribute on the element. */
3128
readonly labelledBy = computed(() => {
32-
const label = this.label();
33-
const labelledBy = this.inputs.labelledBy?.();
3429
const defaultLabelledBy = this.inputs.defaultLabelledBy();
30+
const label = this.label();
31+
const labelledBy = this.inputs.labelledBy();
3532

33+
// Always use any specified labelledby ids.
3634
if (labelledBy && labelledBy.length > 0) {
37-
return labelledBy;
35+
return labelledBy.join(' ');
3836
}
3937

40-
// If an aria-label is provided by developers, do not set aria-labelledby with the
41-
// defaultLabelledBy value because if both attributes are set, aria-labelledby will be used.
42-
if (label) {
43-
return [];
38+
// If an aria-label is provided, do not set aria-labelledby with the defaultLabelledBy value
39+
// because if both attributes are set, aria-labelledby will be used.
40+
if (!label && defaultLabelledBy) {
41+
return defaultLabelledBy;
4442
}
4543

46-
return defaultLabelledBy;
44+
return undefined;
4745
});
4846

49-
constructor(readonly inputs: LabelControlInputs & LabelControlOptionalInputs) {}
47+
constructor(readonly inputs: LabelControlInputs) {}
5048
}

src/aria/private/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './listbox/listbox';
1111
export * from './listbox/option';
1212
export * from './listbox/combobox-listbox';
1313
export * from './menu/menu';
14+
export * from './behaviors/label/label';
1415
export * from './behaviors/signal-like/signal-like';
1516
export * from './tabs/tabs';
1617
export * from './toolbar/toolbar';

src/aria/private/tabs/BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ ts_project(
1111
"//:node_modules/@angular/core",
1212
"//src/aria/private/behaviors/event-manager",
1313
"//src/aria/private/behaviors/expansion",
14-
"//src/aria/private/behaviors/label",
1514
"//src/aria/private/behaviors/list-focus",
1615
"//src/aria/private/behaviors/list-navigation",
1716
"//src/aria/private/behaviors/signal-like",

0 commit comments

Comments
 (0)