Skip to content

Commit 4ce794c

Browse files
authored
feat(cdk-experimental/ui-patterns): add popup behavior (#31550)
* feat(cdk-experimental/ui-patterns): add popup behavior * Adds a new popup behavior to manage the open/close state of a component. * Includes the PopupControl class with open, close, and toggle methods. * Provides comprehensive unit tests for the new behavior. * fixup! feat(cdk-experimental/ui-patterns): add popup behavior * fixup! feat(cdk-experimental/ui-patterns): add popup behavior * fixup! feat(cdk-experimental/ui-patterns): add popup behavior
1 parent e58092b commit 4ce794c

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
load("//tools:defaults.bzl", "ng_web_test_suite", "ts_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_project(
6+
name = "popup",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
deps = [
12+
"//:node_modules/@angular/core",
13+
"//src/cdk-experimental/ui-patterns/behaviors/signal-like",
14+
],
15+
)
16+
17+
ts_project(
18+
name = "unit_test_sources",
19+
testonly = True,
20+
srcs = glob(["**/*.spec.ts"]),
21+
deps = [
22+
":popup",
23+
"//:node_modules/@angular/core",
24+
],
25+
)
26+
27+
ng_web_test_suite(
28+
name = "unit_tests",
29+
deps = [":unit_test_sources"],
30+
)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {signal} from '@angular/core';
10+
import {PopupTypes, PopupControl, PopupControlInputs} from './popup';
11+
12+
type TestInputs = Partial<Pick<PopupControlInputs, 'expanded'>>;
13+
14+
function getPopupControl(inputs: TestInputs = {}): PopupControl {
15+
const expanded = inputs.expanded || signal(false);
16+
const controls = signal('popup-element-id');
17+
const hasPopup = signal(PopupTypes.LISTBOX);
18+
19+
return new PopupControl({
20+
controls,
21+
expanded,
22+
hasPopup,
23+
});
24+
}
25+
26+
describe('Popup Control', () => {
27+
describe('#open', () => {
28+
it('should set expanded to true and popup inert to false', () => {
29+
const control = getPopupControl();
30+
31+
expect(control.inputs.expanded()).toBeFalse();
32+
control.open();
33+
expect(control.inputs.expanded()).toBeTrue();
34+
});
35+
});
36+
37+
describe('#close', () => {
38+
it('should set expanded to false and popup inert to true', () => {
39+
const expanded = signal(true);
40+
const control = getPopupControl({expanded});
41+
42+
expect(control.inputs.expanded()).toBeTrue();
43+
control.close();
44+
expect(control.inputs.expanded()).toBeFalse();
45+
});
46+
});
47+
48+
describe('#toggle', () => {
49+
it('should toggle expanded and popup inert states', () => {
50+
const control = getPopupControl();
51+
52+
expect(control.inputs.expanded()).toBeFalse();
53+
control.toggle();
54+
55+
expect(control.inputs.expanded()).toBeTrue();
56+
control.toggle();
57+
58+
expect(control.inputs.expanded()).toBeFalse();
59+
});
60+
});
61+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {SignalLike, WritableSignalLike} from '../signal-like/signal-like';
10+
11+
/** Valid popup types for aria-haspopup. */
12+
export enum PopupTypes {
13+
MENU = 'menu',
14+
TREE = 'tree',
15+
GRID = 'grid',
16+
DIALOG = 'dialog',
17+
LISTBOX = 'listbox',
18+
}
19+
20+
/** Represents the inputs for the PopupControl behavior. */
21+
export interface PopupControlInputs {
22+
/* Refers to the element that serves as the popup. */
23+
controls: SignalLike<string>;
24+
25+
/* Whether the popup is open or closed. */
26+
expanded: WritableSignalLike<boolean>;
27+
28+
/* Corresponds to the popup type. */
29+
hasPopup: SignalLike<PopupTypes>;
30+
}
31+
32+
/** A behavior that manages the open/close state of a component. */
33+
export class PopupControl {
34+
/** The inputs for the popup behavior, containing the `expanded` state signal. */
35+
constructor(readonly inputs: PopupControlInputs) {}
36+
37+
/** Opens the popup by setting the expanded state to true. */
38+
open(): void {
39+
this.inputs.expanded.set(true);
40+
}
41+
42+
/** Closes the popup by setting the expanded state to false. */
43+
close(): void {
44+
this.inputs.expanded.set(false);
45+
}
46+
47+
/** Toggles the popup's expanded state. */
48+
toggle(): void {
49+
this.inputs.expanded.set(!this.inputs.expanded());
50+
}
51+
}

0 commit comments

Comments
 (0)