diff --git a/.bazelignore b/.bazelignore index 7e56385b36c1..71df476de1d1 100644 --- a/.bazelignore +++ b/.bazelignore @@ -18,6 +18,7 @@ integration/yarn-pnp-compat/.yarn/install-state.gz integration/yarn-pnp-compat/node_modules integration/node_modules +src/aria/node_modules src/cdk-experimental/node_modules src/cdk/node_modules src/components-examples/node_modules diff --git a/.ng-dev/commit-message.mts b/.ng-dev/commit-message.mts index 2b9f9bbba50a..45af3ed66c31 100644 --- a/.ng-dev/commit-message.mts +++ b/.ng-dev/commit-message.mts @@ -9,20 +9,20 @@ export const commitMessage: CommitMessageConfig = { minBodyLengthTypeExcludes: ['docs'], scopes: [ 'multiple', // For when a commit applies to multiple components. - 'cdk-experimental/accordion', + 'aria/accordion', + 'aria/combobox', + 'aria/listbox', + 'aria/menu', + 'aria/radio-group', + 'aria/tabs', + 'aria/toolbar', + 'aria/tree', + 'aria/ui-patterns', 'cdk-experimental/column-resize', - 'cdk-experimental/combobox', - 'cdk-experimental/listbox', - 'cdk-experimental/menu', 'cdk-experimental/popover-edit', - 'cdk-experimental/radio-group', 'cdk-experimental/scrolling', 'cdk-experimental/selection', 'cdk-experimental/table-scroll-container', - 'cdk-experimental/tabs', - 'cdk-experimental/toolbar', - 'cdk-experimental/tree', - 'cdk-experimental/ui-patterns', 'cdk/a11y', 'cdk/accordion', 'cdk/bidi', diff --git a/.ng-dev/google-sync-config.json b/.ng-dev/google-sync-config.json index effc32010c0f..c8fdea37cb24 100644 --- a/.ng-dev/google-sync-config.json +++ b/.ng-dev/google-sync-config.json @@ -10,6 +10,9 @@ "src/material-experimental/**/*.ts", "src/material-experimental/**/*.html", "src/material-experimental/**/*.scss", + "src/aria/**/*.ts", + "src/aria/**/*.html", + "src/aria/**/*.scss", "src/cdk/**/*.ts", "src/cdk/**/*.html", "src/cdk/**/*.scss", diff --git a/.ng-dev/release.mts b/.ng-dev/release.mts index 42ec4ff2ee77..86318b3c3a85 100644 --- a/.ng-dev/release.mts +++ b/.ng-dev/release.mts @@ -18,6 +18,7 @@ import {assertValidNpmPackageOutput} from '../tools/release-checks/npm-package-o * appear in the changelog. */ export const releasePackages = [ + 'aria', 'cdk', 'material', 'google-maps', diff --git a/BUILD.bazel b/BUILD.bazel index 9fe802a69094..3e21ad52e5cc 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,6 +1,7 @@ load("@devinfra//bazel/validation:defs.bzl", "validate_ts_version_matching") load("@npm//:defs.bzl", "npm_link_all_packages") load("//:pkg-externals.bzl", "PKG_EXTERNALS") +load("//src/aria:config.bzl", "ARIA_ENTRYPOINTS") load("//src/cdk:config.bzl", "CDK_ENTRYPOINTS") load("//src/cdk-experimental:config.bzl", "CDK_EXPERIMENTAL_ENTRYPOINTS") load("//src/material:config.bzl", "MATERIAL_ENTRYPOINTS", "MATERIAL_TESTING_ENTRYPOINTS") @@ -26,7 +27,8 @@ genrule( cmd = "echo '%s' > $@" % PKG_EXTERNALS, ) -entryPoints = ["cdk/%s" % e for e in CDK_ENTRYPOINTS] + \ +entryPoints = ["aria/%s" % e for e in ARIA_ENTRYPOINTS] + \ + ["cdk/%s" % e for e in CDK_ENTRYPOINTS] + \ ["cdk-experimental/%s" % e for e in CDK_EXPERIMENTAL_ENTRYPOINTS] + \ ["material/%s" % e for e in MATERIAL_ENTRYPOINTS + MATERIAL_TESTING_ENTRYPOINTS] + \ ["material-experimental/%s" % e for e in MATERIAL_EXPERIMENTAL_ENTRYPOINTS + MATERIAL_EXPERIMENTAL_TESTING_ENTRYPOINTS] diff --git a/MODULE.bazel b/MODULE.bazel index 3d5bb648c068..975bf9dff240 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -77,6 +77,7 @@ npm.npm_translate_lock( "//:package.json", "//:pnpm-workspace.yaml", "//integration:package.json", + "//src/aria:package.json", "//src/cdk:package.json", "//src/cdk-experimental:package.json", "//src/components-examples:package.json", @@ -93,6 +94,10 @@ npm.npm_translate_lock( ], npmrc = "//:.npmrc", package_visibility = { + "@angular/aria": [ + "//integration:__subpackages__", + "//docs:__subpackages__", + ], "@angular/cdk": [ "//integration:__subpackages__", "//docs:__subpackages__", diff --git a/docs/defs.bzl b/docs/defs.bzl index 574f45914c9c..83e51cdc37d7 100644 --- a/docs/defs.bzl +++ b/docs/defs.bzl @@ -19,6 +19,7 @@ COMMON_CONFIG = [ # Project dependencies common across libs/tests DEPS = [ + "//docs:node_modules/@angular/aria", "//docs:node_modules/@angular/cdk", "//docs:node_modules/@angular/cdk-experimental", "//docs:node_modules/@angular/material", diff --git a/docs/package.json b/docs/package.json index b3e289702dba..11cfbf8bcb30 100644 --- a/docs/package.json +++ b/docs/package.json @@ -28,6 +28,7 @@ "npm": "Please use pnpm instead of NPM to install dependencies" }, "dependencies": { + "@angular/aria": "workspace:*", "@angular/cdk": "workspace:*", "@angular/cdk-experimental": "workspace:*", "@angular/common": "catalog:", diff --git a/packages.bzl b/packages.bzl index 04494638ebee..b9e569839c2a 100644 --- a/packages.bzl +++ b/packages.bzl @@ -1,6 +1,7 @@ # Packages which are versioned together on npm ANGULAR_COMPONENTS_SCOPED_PACKAGES = ["@angular/%s" % p for p in [ "material", + "aria", "cdk", "cdk-experimental", "material-experimental", diff --git a/pkg-externals.bzl b/pkg-externals.bzl index c95009e34d54..1c06874b02ff 100644 --- a/pkg-externals.bzl +++ b/pkg-externals.bzl @@ -1,3 +1,4 @@ +load("//src/aria:config.bzl", "ARIA_ENTRYPOINTS") load("//src/cdk:config.bzl", "CDK_ENTRYPOINTS") load("//src/cdk-experimental:config.bzl", "CDK_EXPERIMENTAL_ENTRYPOINTS") load("//src/material:config.bzl", "MATERIAL_ENTRYPOINTS", "MATERIAL_TESTING_ENTRYPOINTS") @@ -28,6 +29,7 @@ PKG_EXTERNALS = [ "@angular/router", # Primary entry-points in the project. + "@angular/aria", "@angular/cdk", "@angular/cdk-experimental", "@angular/google-maps", @@ -58,6 +60,7 @@ PKG_EXTERNALS = [ def setup_entry_point_externals(packageName, entryPoints): PKG_EXTERNALS.extend(["@angular/%s/%s" % (packageName, ep) for ep in entryPoints]) +setup_entry_point_externals("aria", ARIA_ENTRYPOINTS) setup_entry_point_externals("cdk", CDK_ENTRYPOINTS) setup_entry_point_externals("cdk-experimental", CDK_EXPERIMENTAL_ENTRYPOINTS) setup_entry_point_externals("material", MATERIAL_ENTRYPOINTS + MATERIAL_TESTING_ENTRYPOINTS) @@ -68,6 +71,7 @@ setup_entry_point_externals( # External module names in the examples package. Individual examples are grouped # by package and component, so we add configure such entry-points as external. +setup_entry_point_externals("components-examples/aria", ARIA_ENTRYPOINTS) setup_entry_point_externals("components-examples/cdk", CDK_ENTRYPOINTS) setup_entry_point_externals("components-examples/cdk-experimental", CDK_EXPERIMENTAL_ENTRYPOINTS) setup_entry_point_externals( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f02762287b7..8e9ac4e37edd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -347,6 +347,9 @@ importers: docs: dependencies: + '@angular/aria': + specifier: workspace:* + version: link:../src/aria '@angular/cdk': specifier: workspace:* version: link:../src/cdk @@ -517,6 +520,16 @@ importers: specifier: workspace:* version: link:../src/youtube-player + src/aria: + dependencies: + tslib: + specifier: ^2.3.0 + version: 2.8.1 + devDependencies: + '@angular/cdk': + specifier: workspace:* + version: link:../cdk + src/cdk: dependencies: parse5: @@ -542,6 +555,9 @@ importers: specifier: ^2.3.0 version: 2.8.1 devDependencies: + '@angular/aria': + specifier: workspace:* + version: link:../aria '@angular/cdk': specifier: workspace:* version: link:../cdk @@ -560,6 +576,9 @@ importers: src/dev-app: devDependencies: + '@angular/aria': + specifier: workspace:* + version: link:../aria '@angular/cdk': specifier: workspace:* version: link:../cdk diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cd2997ff8016..40b9feb409f1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - . - docs - integration + - src/aria - src/cdk - src/cdk-experimental - src/components-examples diff --git a/scripts/deploy/publish-build-artifacts.sh b/scripts/deploy/publish-build-artifacts.sh index 12c7804da41f..986fc987d3b4 100755 --- a/scripts/deploy/publish-build-artifacts.sh +++ b/scripts/deploy/publish-build-artifacts.sh @@ -17,6 +17,7 @@ fi # Release packages that need to published as snapshots. PACKAGES=( + aria cdk cdk-experimental material diff --git a/scripts/util.mts b/scripts/util.mts index f5031cf0b797..5666eb95f371 100644 --- a/scripts/util.mts +++ b/scripts/util.mts @@ -7,7 +7,13 @@ const commonTypos = new Map([['snackbar', 'snack-bar']]); // List of packages where the specified component could be defined in. The script uses the // first package that contains the component (if no package is specified explicitly). // e.g. "button" will become "material/button", and "overlay" becomes "cdk/overlay". -const orderedGuessPackages = ['material', 'cdk', 'material-experimental', 'cdk-experimental']; +const orderedGuessPackages = [ + 'material', + 'aria', + 'cdk', + 'material-experimental', + 'cdk-experimental', +]; /** * Tries to guess the full name of a package, based on a shorthand name. diff --git a/src/aria/BUILD.bazel b/src/aria/BUILD.bazel new file mode 100644 index 000000000000..d240cb92b248 --- /dev/null +++ b/src/aria/BUILD.bazel @@ -0,0 +1,30 @@ +load("@npm//:defs.bzl", "npm_link_all_packages") +load("//src/aria:config.bzl", "ARIA_TARGETS") +load("//tools:defaults.bzl", "ng_package", "ts_project") + +package(default_visibility = ["//visibility:public"]) + +npm_link_all_packages() + +ts_project( + name = "aria", + srcs = glob( + ["*.ts"], + exclude = ["**/*.spec.ts"], + ), + deps = ["//:node_modules/@angular/core"], +) + +ng_package( + name = "npm_package", + package_name = "@angular/aria", + srcs = ["package.json"], + tags = ["release-package"], + visibility = [ + "//:__pkg__", + "//goldens:__pkg__", + "//integration:__subpackages__", + "//src/material-experimental:__subpackages__", + ], + deps = ARIA_TARGETS, +) diff --git a/src/cdk-experimental/accordion/BUILD.bazel b/src/aria/accordion/BUILD.bazel similarity index 85% rename from src/cdk-experimental/accordion/BUILD.bazel rename to src/aria/accordion/BUILD.bazel index d6c47c0e0b34..2876dab751ed 100644 --- a/src/cdk-experimental/accordion/BUILD.bazel +++ b/src/aria/accordion/BUILD.bazel @@ -7,12 +7,11 @@ ng_project( srcs = [ "accordion.ts", "index.ts", - "public-api.ts", ], deps = [ "//:node_modules/@angular/core", - "//src/cdk-experimental/deferred-content", - "//src/cdk-experimental/ui-patterns", + "//src/aria/deferred-content", + "//src/aria/ui-patterns", "//src/cdk/a11y", "//src/cdk/bidi", ], diff --git a/src/cdk-experimental/accordion/accordion.spec.ts b/src/aria/accordion/accordion.spec.ts similarity index 95% rename from src/cdk-experimental/accordion/accordion.spec.ts rename to src/aria/accordion/accordion.spec.ts index 54da825ad6d7..a462023a5db2 100644 --- a/src/cdk-experimental/accordion/accordion.spec.ts +++ b/src/aria/accordion/accordion.spec.ts @@ -3,19 +3,14 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {provideFakeDirectionality, runAccessibilityChecks} from '@angular/cdk/testing/private'; import {_IdGenerator} from '@angular/cdk/a11y'; -import { - CdkAccordionGroup, - CdkAccordionTrigger, - CdkAccordionPanel, - CdkAccordionContent, -} from './accordion'; - -describe('CdkAccordionGroup', () => { +import {AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent} from './accordion'; + +describe('AccordionGroup', () => { let fixture: ComponentFixture; let groupDebugElement: DebugElement; let triggerDebugElements: DebugElement[]; let panelDebugElements: DebugElement[]; - let groupInstance: CdkAccordionGroup; + let groupInstance: AccordionGroup; let triggerElements: HTMLElement[]; let panelElements: HTMLElement[]; @@ -72,17 +67,17 @@ describe('CdkAccordionGroup', () => { } function defineTestVariables(currentFixture: ComponentFixture) { - groupDebugElement = currentFixture.debugElement.query(By.directive(CdkAccordionGroup)); - triggerDebugElements = currentFixture.debugElement.queryAll(By.directive(CdkAccordionTrigger)); - panelDebugElements = currentFixture.debugElement.queryAll(By.directive(CdkAccordionPanel)); + groupDebugElement = currentFixture.debugElement.query(By.directive(AccordionGroup)); + triggerDebugElements = currentFixture.debugElement.queryAll(By.directive(AccordionTrigger)); + panelDebugElements = currentFixture.debugElement.queryAll(By.directive(AccordionPanel)); - groupInstance = groupDebugElement.injector.get(CdkAccordionGroup); + groupInstance = groupDebugElement.injector.get(AccordionGroup); triggerElements = triggerDebugElements.map(el => el.nativeElement); panelElements = panelDebugElements.map(el => el.nativeElement); } function isTriggerActive(target: HTMLElement): boolean { - return target.classList.contains('cdk-active'); + return target.getAttribute('data-active') === 'true'; } function isTriggerExpanded(target: HTMLElement): boolean { @@ -102,7 +97,7 @@ describe('CdkAccordionGroup', () => { }); describe('ARIA attributes and roles', () => { - describe('CdkAccordionTrigger', () => { + describe('AccordionTrigger', () => { beforeEach(() => { configureAccordionComponent(); }); @@ -141,7 +136,7 @@ describe('CdkAccordionGroup', () => { }); }); - describe('CdkAccordionPanel', () => { + describe('AccordionPanel', () => { beforeEach(() => { configureAccordionComponent(); }); @@ -386,7 +381,7 @@ describe('CdkAccordionGroup', () => { @Component({ template: `
{ @for (item of items(); track item.value) {
- + {{ item.content }}
@@ -412,7 +407,7 @@ describe('CdkAccordionGroup', () => { }
`, - imports: [CdkAccordionGroup, CdkAccordionTrigger, CdkAccordionPanel, CdkAccordionContent], + imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent], }) class AccordionGroupExample { items = signal([ diff --git a/src/cdk-experimental/accordion/accordion.ts b/src/aria/accordion/accordion.ts similarity index 80% rename from src/cdk-experimental/accordion/accordion.ts rename to src/aria/accordion/accordion.ts index 8ddd363f4993..504c430a957f 100644 --- a/src/cdk-experimental/accordion/accordion.ts +++ b/src/aria/accordion/accordion.ts @@ -21,20 +21,20 @@ import { } from '@angular/core'; import {_IdGenerator} from '@angular/cdk/a11y'; import {Directionality} from '@angular/cdk/bidi'; -import {DeferredContent, DeferredContentAware} from '@angular/cdk-experimental/deferred-content'; +import {DeferredContent, DeferredContentAware} from '@angular/aria/deferred-content'; import { AccordionGroupPattern, AccordionPanelPattern, AccordionTriggerPattern, -} from '../ui-patterns'; +} from '@angular/aria/ui-patterns'; /** * Represents the content panel of an accordion item. It is controlled by an - * associated `CdkAccordionTrigger`. + * associated `AccordionTrigger`. */ @Directive({ - selector: '[cdkAccordionPanel]', - exportAs: 'cdkAccordionPanel', + selector: '[ngAccordionPanel]', + exportAs: 'ngAccordionPanel', hostDirectives: [ { directive: DeferredContentAware, @@ -42,24 +42,24 @@ import { }, ], host: { - 'class': 'cdk-accordion-panel', + 'class': 'ng-accordion-panel', 'role': 'region', '[attr.id]': 'pattern.id()', '[attr.aria-labelledby]': 'pattern.accordionTrigger()?.id()', '[attr.inert]': 'pattern.hidden() ? true : null', }, }) -export class CdkAccordionPanel { +export class AccordionPanel { /** The DeferredContentAware host directive. */ private readonly _deferredContentAware = inject(DeferredContentAware); /** A global unique identifier for the panel. */ - private readonly _id = inject(_IdGenerator).getId('cdk-accordion-trigger-'); + private readonly _id = inject(_IdGenerator).getId('accordion-trigger-'); /** A local unique identifier for the panel, used to match with its trigger's value. */ value = input.required(); - /** The parent accordion trigger pattern that controls this panel. This is set by CdkAccordionGroup. */ + /** The parent accordion trigger pattern that controls this panel. This is set by AccordionGroup. */ readonly accordionTrigger: WritableSignal = signal(undefined); @@ -80,14 +80,14 @@ export class CdkAccordionPanel { /** * Represents the trigger button for an accordion item. It controls the expansion - * state of an associated `CdkAccordionPanel`. + * state of an associated `AccordionPanel`. */ @Directive({ - selector: '[cdkAccordionTrigger]', - exportAs: 'cdkAccordionTrigger', + selector: '[ngAccordionTrigger]', + exportAs: 'ngAccordionTrigger', host: { - 'class': 'cdk-accordion-trigger', - '[class.cdk-active]': 'pattern.active()', + 'class': 'ng-accordion-trigger', + '[attr.data-active]': 'pattern.active()', 'role': 'button', '[id]': 'pattern.id()', '[attr.aria-expanded]': 'pattern.expanded()', @@ -100,15 +100,15 @@ export class CdkAccordionPanel { '(focusin)': 'pattern.onFocus($event)', }, }) -export class CdkAccordionTrigger { +export class AccordionTrigger { /** A global unique identifier for the trigger. */ - private readonly _id = inject(_IdGenerator).getId('cdk-accordion-trigger-'); + private readonly _id = inject(_IdGenerator).getId('ng-accordion-trigger-'); /** A reference to the trigger element. */ private readonly _elementRef = inject(ElementRef); - /** The parent CdkAccordionGroup. */ - private readonly _accordionGroup = inject(CdkAccordionGroup); + /** The parent AccordionGroup. */ + private readonly _accordionGroup = inject(AccordionGroup); /** A local unique identifier for the trigger, used to match with its panel's value. */ value = input.required(); @@ -123,7 +123,7 @@ export class CdkAccordionTrigger { */ readonly hardDisabled = computed(() => this.pattern.disabled() && this.pattern.tabindex() < 0); - /** The accordion panel pattern controlled by this trigger. This is set by CdkAccordionGroup. */ + /** The accordion panel pattern controlled by this trigger. This is set by AccordionGroup. */ readonly accordionPanel: WritableSignal = signal(undefined); /** The UI pattern instance for this trigger. */ @@ -142,21 +142,21 @@ export class CdkAccordionTrigger { * interactions of the accordion, such as keyboard navigation and expansion mode. */ @Directive({ - selector: '[cdkAccordionGroup]', - exportAs: 'cdkAccordionGroup', + selector: '[ngAccordionGroup]', + exportAs: 'ngAccordionGroup', host: { - 'class': 'cdk-accordion-group', + 'class': 'ng-accordion-group', }, }) -export class CdkAccordionGroup { +export class AccordionGroup { /** A reference to the group element. */ private readonly _elementRef = inject(ElementRef); - /** The CdkAccordionTriggers nested inside this group. */ - protected readonly _triggers = contentChildren(CdkAccordionTrigger, {descendants: true}); + /** The AccordionTriggers nested inside this group. */ + protected readonly _triggers = contentChildren(AccordionTrigger, {descendants: true}); - /** The CdkAccordionPanels nested inside this group. */ - protected readonly _panels = contentChildren(CdkAccordionPanel, {descendants: true}); + /** The AccordionPanels nested inside this group. */ + protected readonly _panels = contentChildren(AccordionPanel, {descendants: true}); /** The text direction (ltr or rtl). */ readonly textDirection = inject(Directionality).valueSignal; @@ -208,10 +208,10 @@ export class CdkAccordionGroup { /** * A structural directive that marks the `ng-template` to be used as the content - * for a `CdkAccordionPanel`. This content can be lazily loaded. + * for a `AccordionPanel`. This content can be lazily loaded. */ @Directive({ - selector: 'ng-template[cdkAccordionContent]', + selector: 'ng-template[ngAccordionContent]', hostDirectives: [DeferredContent], }) -export class CdkAccordionContent {} +export class AccordionContent {} diff --git a/src/aria/accordion/index.ts b/src/aria/accordion/index.ts new file mode 100644 index 000000000000..66aa4d173aa8 --- /dev/null +++ b/src/aria/accordion/index.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +export {AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent} from './accordion'; diff --git a/src/cdk-experimental/combobox/BUILD.bazel b/src/aria/combobox/BUILD.bazel similarity index 81% rename from src/cdk-experimental/combobox/BUILD.bazel rename to src/aria/combobox/BUILD.bazel index 8cef8563f576..be11bac48c0c 100644 --- a/src/cdk-experimental/combobox/BUILD.bazel +++ b/src/aria/combobox/BUILD.bazel @@ -10,8 +10,8 @@ ng_project( ), deps = [ "//:node_modules/@angular/core", - "//src/cdk-experimental/deferred-content", - "//src/cdk-experimental/ui-patterns", + "//src/aria/deferred-content", + "//src/aria/ui-patterns", ], ) @@ -28,8 +28,8 @@ ts_project( "//:node_modules/@angular/core", "//:node_modules/@angular/platform-browser", "//:node_modules/axe-core", - "//src/cdk-experimental/listbox", - "//src/cdk-experimental/tree", + "//src/aria/listbox", + "//src/aria/tree", "//src/cdk/testing/private", ], ) diff --git a/src/cdk-experimental/combobox/combobox.spec.ts b/src/aria/combobox/combobox.spec.ts similarity index 95% rename from src/cdk-experimental/combobox/combobox.spec.ts rename to src/aria/combobox/combobox.spec.ts index c3198de00b30..e2e010e01efc 100644 --- a/src/cdk-experimental/combobox/combobox.spec.ts +++ b/src/aria/combobox/combobox.spec.ts @@ -1,20 +1,10 @@ import {Component, computed, DebugElement, signal} from '@angular/core'; import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; -import { - CdkCombobox, - CdkComboboxInput, - CdkComboboxPopup, - CdkComboboxPopupContainer, -} from '@angular/cdk-experimental/combobox'; -import {CdkListbox, CdkOption} from '@angular/cdk-experimental/listbox'; +import {Combobox, ComboboxInput, ComboboxPopup, ComboboxPopupContainer} from '../combobox'; +import {Listbox, Option} from '../listbox'; import {runAccessibilityChecks} from '@angular/cdk/testing/private'; -import { - CdkTree, - CdkTreeItem, - CdkTreeItemGroup, - CdkTreeItemGroupContent, -} from '@angular/cdk-experimental/tree'; +import {Tree, TreeItem, TreeItemGroup, TreeItemGroupContent} from '../tree'; import {NgTemplateOutlet} from '@angular/common'; describe('Combobox', () => { @@ -76,20 +66,20 @@ describe('Combobox', () => { } function defineTestVariables() { - const inputDebugElement = fixture.debugElement.query(By.directive(CdkComboboxInput)); + const inputDebugElement = fixture.debugElement.query(By.directive(ComboboxInput)); inputElement = inputDebugElement.nativeElement as HTMLInputElement; } function getOption(text: string): HTMLElement | null { const options = fixture.debugElement - .queryAll(By.directive(CdkOption)) + .queryAll(By.directive(Option)) .map((debugEl: DebugElement) => debugEl.nativeElement as HTMLElement); return options.find(option => option.textContent?.trim() === text) || null; } function getOptions(): HTMLElement[] { return fixture.debugElement - .queryAll(By.directive(CdkOption)) + .queryAll(By.directive(Option)) .map((debugEl: DebugElement) => debugEl.nativeElement as HTMLElement); } @@ -109,7 +99,7 @@ describe('Combobox', () => { it('should set aria-controls to the listbox id', () => { focus(); - const listbox = fixture.debugElement.query(By.directive(CdkListbox)).nativeElement; + const listbox = fixture.debugElement.query(By.directive(Listbox)).nativeElement; expect(inputElement.getAttribute('aria-controls')).toBe(listbox.id); }); @@ -131,7 +121,7 @@ describe('Combobox', () => { it('should set aria-multiselectable to false on the listbox', () => { focus(); - const listbox = fixture.debugElement.query(By.directive(CdkListbox)).nativeElement; + const listbox = fixture.debugElement.query(By.directive(Listbox)).nativeElement; expect(listbox.getAttribute('aria-multiselectable')).toBe('false'); }); @@ -563,26 +553,26 @@ describe('Combobox', () => { } function defineTestVariables() { - const inputDebugElement = fixture.debugElement.query(By.directive(CdkComboboxInput)); + const inputDebugElement = fixture.debugElement.query(By.directive(ComboboxInput)); inputElement = inputDebugElement.nativeElement as HTMLInputElement; } function getTreeItem(text: string): HTMLElement | null { const items = fixture.debugElement - .queryAll(By.directive(CdkTreeItem)) + .queryAll(By.directive(TreeItem)) .map((debugEl: DebugElement) => debugEl.nativeElement as HTMLElement); return items.find(item => item.textContent?.trim() === text) || null; } function getTreeItems(): HTMLElement[] { return fixture.debugElement - .queryAll(By.directive(CdkTreeItem)) + .queryAll(By.directive(TreeItem)) .map((debugEl: DebugElement) => debugEl.nativeElement as HTMLElement); } function getVisibleTreeItems(): HTMLElement[] { return fixture.debugElement - .queryAll(By.directive(CdkTreeItem)) + .queryAll(By.directive(TreeItem)) .map((debugEl: DebugElement) => debugEl.nativeElement as HTMLElement) .filter(el => { if (el.parentElement?.role === 'group') { @@ -606,7 +596,7 @@ describe('Combobox', () => { it('should set aria-controls to the tree id', () => { down(); - const tree = fixture.debugElement.query(By.directive(CdkTree)).nativeElement; + const tree = fixture.debugElement.query(By.directive(Tree)).nativeElement; expect(inputElement.getAttribute('aria-controls')).toBe(tree.id); }); @@ -1018,21 +1008,21 @@ describe('Combobox', () => { @Component({ template: `
- -
+ +
@for (option of options(); track option) {
@@ -1043,14 +1033,7 @@ describe('Combobox', () => {
`, - imports: [ - CdkCombobox, - CdkComboboxInput, - CdkComboboxPopup, - CdkComboboxPopupContainer, - CdkListbox, - CdkOption, - ], + imports: [Combobox, ComboboxInput, ComboboxPopup, ComboboxPopupContainer, Listbox, Option], }) class ComboboxListboxExample { value = signal([]); @@ -1067,19 +1050,19 @@ class ComboboxListboxExample { @Component({ template: `
- -
    + +
      @for (node of nodes; track node.name) { -
    • {{ node.name }}
    • @if (node.children) { -
        - +
          + `, imports: [ - CdkCombobox, - CdkComboboxInput, - CdkComboboxPopupContainer, - CdkTree, - CdkTreeItem, - CdkTreeItemGroup, - CdkTreeItemGroupContent, + Combobox, + ComboboxInput, + ComboboxPopupContainer, + Tree, + TreeItem, + TreeItemGroup, + TreeItemGroupContent, NgTemplateOutlet, ], }) diff --git a/src/cdk-experimental/combobox/combobox.ts b/src/aria/combobox/combobox.ts similarity index 81% rename from src/cdk-experimental/combobox/combobox.ts rename to src/aria/combobox/combobox.ts index 38d10e19bf48..de5ed5d008d8 100644 --- a/src/cdk-experimental/combobox/combobox.ts +++ b/src/aria/combobox/combobox.ts @@ -18,12 +18,16 @@ import { untracked, WritableSignal, } from '@angular/core'; -import {DeferredContent, DeferredContentAware} from '@angular/cdk-experimental/deferred-content'; -import {ComboboxPattern, ComboboxListboxControls, ComboboxTreeControls} from '../ui-patterns'; +import {DeferredContent, DeferredContentAware} from '@angular/aria/deferred-content'; +import { + ComboboxPattern, + ComboboxListboxControls, + ComboboxTreeControls, +} from '@angular/aria/ui-patterns'; @Directive({ - selector: '[cdkCombobox]', - exportAs: 'cdkCombobox', + selector: '[ngCombobox]', + exportAs: 'ngCombobox', hostDirectives: [ { directive: DeferredContentAware, @@ -39,7 +43,7 @@ import {ComboboxPattern, ComboboxListboxControls, ComboboxTreeControls} from '.. '(focusout)': 'pattern.onFocusOut($event)', }, }) -export class CdkCombobox { +export class Combobox { /** The element that the combobox is attached to. */ private readonly _elementRef = inject(ElementRef); @@ -47,7 +51,7 @@ export class CdkCombobox { private readonly _deferredContentAware = inject(DeferredContentAware, {optional: true}); /** The combobox popup. */ - readonly popup = contentChild>(CdkComboboxPopup); + readonly popup = contentChild>(ComboboxPopup); /** The filter mode for the combobox. */ filterMode = input<'manual' | 'auto-select' | 'highlight'>('manual'); @@ -86,8 +90,8 @@ export class CdkCombobox { } @Directive({ - selector: 'input[cdkComboboxInput]', - exportAs: 'cdkComboboxInput', + selector: 'input[ngComboboxInput]', + exportAs: 'ngComboboxInput', host: { 'role': 'combobox', '[value]': 'value()', @@ -98,12 +102,12 @@ export class CdkCombobox { '[attr.aria-autocomplete]': 'combobox.pattern.autocomplete()', }, }) -export class CdkComboboxInput { +export class ComboboxInput { /** The element that the combobox is attached to. */ private readonly _elementRef = inject>(ElementRef); /** The combobox that the input belongs to. */ - readonly combobox = inject(CdkCombobox); + readonly combobox = inject(Combobox); /** The value of the input. */ value = model(''); @@ -123,19 +127,19 @@ export class CdkComboboxInput { } @Directive({ - selector: 'ng-template[cdkComboboxPopupContainer]', - exportAs: 'cdkComboboxPopupContainer', + selector: 'ng-template[ngComboboxPopupContainer]', + exportAs: 'ngComboboxPopupContainer', hostDirectives: [DeferredContent], }) -export class CdkComboboxPopupContainer {} +export class ComboboxPopupContainer {} @Directive({ - selector: '[cdkComboboxPopup]', - exportAs: 'cdkComboboxPopup', + selector: '[ngComboboxPopup]', + exportAs: 'ngComboboxPopup', }) -export class CdkComboboxPopup { +export class ComboboxPopup { /** The combobox that the popup belongs to. */ - readonly combobox = inject>(CdkCombobox, {optional: true}); + readonly combobox = inject>(Combobox, {optional: true}); /** The controls the popup exposes to the combobox. */ readonly controls = signal< diff --git a/src/aria/combobox/index.ts b/src/aria/combobox/index.ts new file mode 100644 index 000000000000..c859f6cc01de --- /dev/null +++ b/src/aria/combobox/index.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +export {Combobox, ComboboxInput, ComboboxPopup, ComboboxPopupContainer} from './combobox'; diff --git a/src/aria/config.bzl b/src/aria/config.bzl new file mode 100644 index 000000000000..5f4c21a67bf8 --- /dev/null +++ b/src/aria/config.bzl @@ -0,0 +1,16 @@ +# List of all entry-points of the Angular Aria package. +ARIA_ENTRYPOINTS = [ + "accordion", + "combobox", + "deferred-content", + "listbox", + "radio-group", + "tabs", + "toolbar", + "tree", + "ui-patterns", +] + +# List of all entry-point targets of the Angular Aria package. +ARIA_TARGETS = ["//src/aria"] + \ + ["//src/aria/%s" % ep for ep in ARIA_ENTRYPOINTS] diff --git a/src/cdk-experimental/deferred-content/BUILD.bazel b/src/aria/deferred-content/BUILD.bazel similarity index 96% rename from src/cdk-experimental/deferred-content/BUILD.bazel rename to src/aria/deferred-content/BUILD.bazel index 50e55dd06ef1..3fc4d24459d8 100644 --- a/src/cdk-experimental/deferred-content/BUILD.bazel +++ b/src/aria/deferred-content/BUILD.bazel @@ -7,7 +7,6 @@ ng_project( srcs = [ "deferred-content.ts", "index.ts", - "public-api.ts", ], deps = [ "//:node_modules/@angular/core", diff --git a/src/cdk-experimental/deferred-content/deferred-content.spec.ts b/src/aria/deferred-content/deferred-content.spec.ts similarity index 100% rename from src/cdk-experimental/deferred-content/deferred-content.spec.ts rename to src/aria/deferred-content/deferred-content.spec.ts diff --git a/src/cdk-experimental/deferred-content/deferred-content.ts b/src/aria/deferred-content/deferred-content.ts similarity index 94% rename from src/cdk-experimental/deferred-content/deferred-content.ts rename to src/aria/deferred-content/deferred-content.ts index f105ffcbad5a..d8f347ba53b9 100644 --- a/src/cdk-experimental/deferred-content/deferred-content.ts +++ b/src/aria/deferred-content/deferred-content.ts @@ -34,10 +34,10 @@ export class DeferredContentAware { * * ```ts * @Directive({ - * selector: 'ng-template[cdkAccordionContent]', + * selector: 'ng-template[AccordionContent]', * hostDirectives: [DeferredContent], * }) - * class CdkAccordionContent {} + * class AccordionContent {} * ``` */ @Directive() diff --git a/src/cdk-experimental/deferred-content/public-api.ts b/src/aria/deferred-content/index.ts similarity index 100% rename from src/cdk-experimental/deferred-content/public-api.ts rename to src/aria/deferred-content/index.ts diff --git a/src/cdk-experimental/accordion/index.ts b/src/aria/index.ts similarity index 100% rename from src/cdk-experimental/accordion/index.ts rename to src/aria/index.ts diff --git a/src/cdk-experimental/listbox/BUILD.bazel b/src/aria/listbox/BUILD.bazel similarity index 89% rename from src/cdk-experimental/listbox/BUILD.bazel rename to src/aria/listbox/BUILD.bazel index 481b2cfa9ed7..62c65c2faac8 100644 --- a/src/cdk-experimental/listbox/BUILD.bazel +++ b/src/aria/listbox/BUILD.bazel @@ -10,8 +10,8 @@ ng_project( ), deps = [ "//:node_modules/@angular/core", - "//src/cdk-experimental/combobox", - "//src/cdk-experimental/ui-patterns", + "//src/aria/combobox", + "//src/aria/ui-patterns", "//src/cdk/a11y", "//src/cdk/bidi", ], diff --git a/src/cdk-experimental/tabs/index.ts b/src/aria/listbox/index.ts similarity index 82% rename from src/cdk-experimental/tabs/index.ts rename to src/aria/listbox/index.ts index 52b3c7a5156f..a21d11c2d475 100644 --- a/src/cdk-experimental/tabs/index.ts +++ b/src/aria/listbox/index.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.dev/license */ -export * from './public-api'; +export {Listbox, Option} from './listbox'; diff --git a/src/cdk-experimental/listbox/listbox.spec.ts b/src/aria/listbox/listbox.spec.ts similarity index 98% rename from src/cdk-experimental/listbox/listbox.spec.ts rename to src/aria/listbox/listbox.spec.ts index 4aa1935e4063..6241478357f3 100644 --- a/src/cdk-experimental/listbox/listbox.spec.ts +++ b/src/aria/listbox/listbox.spec.ts @@ -1,5 +1,5 @@ import {Component, DebugElement, signal} from '@angular/core'; -import {CdkListbox, CdkOption} from './listbox'; +import {Listbox, Option} from './listbox'; import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Direction} from '@angular/cdk/bidi'; @@ -12,11 +12,11 @@ interface ModifierKeys { metaKey?: boolean; } -describe('CdkListbox', () => { +describe('Listbox', () => { let fixture: ComponentFixture; let listboxDebugElement: DebugElement; let optionDebugElements: DebugElement[]; - let listboxInstance: CdkListbox; + let listboxInstance: Listbox; let listboxElement: HTMLElement; let optionElements: HTMLElement[]; @@ -105,9 +105,9 @@ describe('CdkListbox', () => { } function defineTestVariables(fixture: ComponentFixture) { - listboxDebugElement = fixture.debugElement.query(By.directive(CdkListbox)); - optionDebugElements = fixture.debugElement.queryAll(By.directive(CdkOption)); - listboxInstance = listboxDebugElement.injector.get>(CdkListbox); + listboxDebugElement = fixture.debugElement.query(By.directive(Listbox)); + optionDebugElements = fixture.debugElement.queryAll(By.directive(Option)); + listboxInstance = listboxDebugElement.injector.get>(Listbox); listboxElement = listboxDebugElement.nativeElement; optionElements = optionDebugElements.map(option => option.nativeElement); } @@ -742,7 +742,7 @@ interface TestOption { @Component({ template: `
            @for (option of options(); track option.value) { -
          • {{ option.label }}
          • +
          • {{ option.label }}
          • }
          `, - imports: [CdkListbox, CdkOption], + imports: [Listbox, Option], }) class ListboxExample { options = signal([ @@ -784,12 +784,12 @@ class ListboxExample { @Component({ template: ` -
            -
          • 0
          • -
          • 1
          • -
          • 2
          • +
              +
            • 0
            • +
            • 1
            • +
            • 2
            `, - imports: [CdkListbox, CdkOption], + imports: [Listbox, Option], }) class DefaultListboxExample {} diff --git a/src/cdk-experimental/listbox/listbox.ts b/src/aria/listbox/listbox.ts similarity index 83% rename from src/cdk-experimental/listbox/listbox.ts rename to src/aria/listbox/listbox.ts index a8db327eebe3..3900ecea35b5 100644 --- a/src/cdk-experimental/listbox/listbox.ts +++ b/src/aria/listbox/listbox.ts @@ -19,32 +19,32 @@ import { signal, untracked, } from '@angular/core'; -import {ComboboxListboxPattern, ListboxPattern, OptionPattern} from '../ui-patterns'; +import {ComboboxListboxPattern, ListboxPattern, OptionPattern} from '@angular/aria/ui-patterns'; import {Directionality} from '@angular/cdk/bidi'; import {toSignal} from '@angular/core/rxjs-interop'; import {_IdGenerator} from '@angular/cdk/a11y'; -import {CdkComboboxPopup} from '../combobox'; +import {ComboboxPopup} from '../combobox'; /** * A listbox container. * - * Listboxes are used to display a list of items for a user to select from. The CdkListbox is meant - * to be used in conjunction with CdkOption as follows: + * Listboxes are used to display a list of items for a user to select from. The Listbox is meant + * to be used in conjunction with Option as follows: * * ```html - *
              - *
            • Item 1
            • - *
            • Item 2
            • - *
            • Item 3
            • + *
                + *
              • Item 1
              • + *
              • Item 2
              • + *
              • Item 3
              • *
              * ``` */ @Directive({ - selector: '[cdkListbox]', - exportAs: 'cdkListbox', + selector: '[ngListbox]', + exportAs: 'ngListbox', host: { 'role': 'listbox', - 'class': 'cdk-listbox', + 'class': 'ng-listbox', '[attr.id]': 'id()', '[attr.tabindex]': 'pattern.tabindex()', '[attr.aria-readonly]': 'pattern.readonly()', @@ -56,18 +56,18 @@ import {CdkComboboxPopup} from '../combobox'; '(pointerdown)': 'pattern.onPointerdown($event)', '(focusin)': 'onFocus()', }, - hostDirectives: [{directive: CdkComboboxPopup}], + hostDirectives: [{directive: ComboboxPopup}], }) -export class CdkListbox { +export class Listbox { /** A unique identifier for the listbox. */ - private readonly _generatedId = inject(_IdGenerator).getId('cdk-listbox-'); + private readonly _generatedId = inject(_IdGenerator).getId('ng-listbox-'); // TODO(wagnermaciel): https://github.com/angular/components/pull/30495#discussion_r1972601144. /** A unique identifier for the listbox. */ protected id = computed(() => this._generatedId); /** A reference to the parent combobox popup, if one exists. */ - private readonly _popup = inject>(CdkComboboxPopup, { + private readonly _popup = inject>(ComboboxPopup, { optional: true, }); @@ -77,16 +77,16 @@ export class CdkListbox { /** The directionality (LTR / RTL) context for the application (or a subtree of it). */ private readonly _directionality = inject(Directionality); - /** The CdkOptions nested inside of the CdkListbox. */ - private readonly _cdkOptions = contentChildren(CdkOption, {descendants: true}); + /** The Options nested inside of the Listbox. */ + private readonly _options = contentChildren(Option, {descendants: true}); /** A signal wrapper for directionality. */ protected textDirection = toSignal(this._directionality.change, { initialValue: this._directionality.value, }); - /** The Option UIPatterns of the child CdkOptions. */ - protected items = computed(() => this._cdkOptions().map(option => option.pattern)); + /** The Option UIPatterns of the child Options. */ + protected items = computed(() => this._options().map(option => option.pattern)); /** Whether the list is vertically or horizontally oriented. */ orientation = input<'vertical' | 'horizontal'>('vertical'); @@ -185,29 +185,29 @@ export class CdkListbox { } } -/** A selectable option in a CdkListbox. */ +/** A selectable option in a Listbox. */ @Directive({ - selector: '[cdkOption]', - exportAs: 'cdkOption', + selector: '[ngOption]', + exportAs: 'ngOption', host: { 'role': 'option', - 'class': 'cdk-option', - '[class.cdk-active]': 'pattern.active()', + 'class': 'ng-option', + '[attr.data-active]': 'pattern.active()', '[attr.id]': 'pattern.id()', '[attr.tabindex]': 'pattern.tabindex()', '[attr.aria-selected]': 'pattern.selected()', '[attr.aria-disabled]': 'pattern.disabled()', }, }) -export class CdkOption { +export class Option { /** A reference to the option element. */ private readonly _elementRef = inject(ElementRef); - /** The parent CdkListbox. */ - private readonly _cdkListbox = inject(CdkListbox); + /** The parent Listbox. */ + private readonly _listbox = inject(Listbox); /** A unique identifier for the option. */ - private readonly _generatedId = inject(_IdGenerator).getId('cdk-option-'); + private readonly _generatedId = inject(_IdGenerator).getId('ng-option-'); // TODO(wagnermaciel): https://github.com/angular/components/pull/30495#discussion_r1972601144. /** A unique identifier for the option. */ @@ -219,7 +219,7 @@ export class CdkOption { protected searchTerm = computed(() => this.label() ?? this.element().textContent); /** The parent Listbox UIPattern. */ - protected listbox = computed(() => this._cdkListbox.pattern); + protected listbox = computed(() => this._listbox.pattern); /** A reference to the option element to be focused on navigation. */ protected element = computed(() => this._elementRef.nativeElement); diff --git a/src/aria/package.json b/src/aria/package.json new file mode 100644 index 000000000000..89f7f311057d --- /dev/null +++ b/src/aria/package.json @@ -0,0 +1,28 @@ +{ + "name": "@angular/aria", + "version": "0.0.0-PLACEHOLDER", + "description": "Angular Aria", + "repository": { + "type": "git", + "url": "https://github.com/angular/components.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/components/issues" + }, + "homepage": "https://github.com/angular/components#readme", + "peerDependencies": { + "@angular/cdk": "0.0.0-PLACEHOLDER", + "@angular/core": "0.0.0-NG" + }, + "devDependencies": { + "@angular/cdk": "workspace:*" + }, + "dependencies": { + "tslib": "^2.3.0" + }, + "ng-update": { + "packageGroup": "NG_UPDATE_PACKAGE_GROUP" + }, + "sideEffects": false +} diff --git a/src/cdk-experimental/deferred-content/index.ts b/src/aria/public-api.ts similarity index 87% rename from src/cdk-experimental/deferred-content/index.ts rename to src/aria/public-api.ts index 52b3c7a5156f..33bc1f31c7bc 100644 --- a/src/cdk-experimental/deferred-content/index.ts +++ b/src/aria/public-api.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.dev/license */ -export * from './public-api'; +export * from './version'; diff --git a/src/cdk-experimental/radio-group/BUILD.bazel b/src/aria/radio-group/BUILD.bazel similarity index 89% rename from src/cdk-experimental/radio-group/BUILD.bazel rename to src/aria/radio-group/BUILD.bazel index ff5f155007fa..74a189efbb42 100644 --- a/src/cdk-experimental/radio-group/BUILD.bazel +++ b/src/aria/radio-group/BUILD.bazel @@ -10,8 +10,8 @@ ng_project( ), deps = [ "//:node_modules/@angular/core", - "//src/cdk-experimental/toolbar", - "//src/cdk-experimental/ui-patterns", + "//src/aria/toolbar", + "//src/aria/ui-patterns", "//src/cdk/a11y", "//src/cdk/bidi", ], diff --git a/src/cdk-experimental/listbox/index.ts b/src/aria/radio-group/index.ts similarity index 78% rename from src/cdk-experimental/listbox/index.ts rename to src/aria/radio-group/index.ts index 52b3c7a5156f..34aab88aafe3 100644 --- a/src/cdk-experimental/listbox/index.ts +++ b/src/aria/radio-group/index.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.dev/license */ -export * from './public-api'; +export {RadioGroup, RadioButton} from './radio-group'; diff --git a/src/cdk-experimental/radio-group/radio-group.spec.ts b/src/aria/radio-group/radio-group.spec.ts similarity index 96% rename from src/cdk-experimental/radio-group/radio-group.spec.ts rename to src/aria/radio-group/radio-group.spec.ts index ac2639fbf84a..b0643cd5a1cf 100644 --- a/src/cdk-experimental/radio-group/radio-group.spec.ts +++ b/src/aria/radio-group/radio-group.spec.ts @@ -1,15 +1,15 @@ import {Component, DebugElement, signal} from '@angular/core'; -import {CdkRadioButton, CdkRadioGroup} from './radio-group'; +import {RadioButton, RadioGroup} from './radio-group'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Direction} from '@angular/cdk/bidi'; import {provideFakeDirectionality, runAccessibilityChecks} from '@angular/cdk/testing/private'; -describe('CdkRadioGroup', () => { +describe('RadioGroup', () => { let fixture: ComponentFixture; let radioGroup: DebugElement; let radioButtons: DebugElement[]; - let radioGroupInstance: CdkRadioGroup; + let radioGroupInstance: RadioGroup; let radioGroupElement: HTMLElement; let radioButtonElements: HTMLElement[]; @@ -92,9 +92,9 @@ describe('CdkRadioGroup', () => { } function defineTestVariables(fixture: ComponentFixture) { - radioGroup = fixture.debugElement.query(By.directive(CdkRadioGroup)); - radioButtons = fixture.debugElement.queryAll(By.directive(CdkRadioButton)); - radioGroupInstance = radioGroup.injector.get>(CdkRadioGroup); + radioGroup = fixture.debugElement.query(By.directive(RadioGroup)); + radioButtons = fixture.debugElement.queryAll(By.directive(RadioButton)); + radioGroupInstance = radioGroup.injector.get>(RadioGroup); radioGroupElement = radioGroup.nativeElement; radioButtonElements = radioButtons.map(radioButton => radioButton.nativeElement); } @@ -532,13 +532,13 @@ interface TestOption { [focusMode]="focusMode" [orientation]="orientation" [skipDisabled]="skipDisabled" - cdkRadioGroup> + ngRadioGroup> @for (option of options(); track option.value) { -
              {{ option.label }}
              +
              {{ option.label }}
              }
`, - imports: [CdkRadioGroup, CdkRadioButton], + imports: [RadioGroup, RadioButton], }) class RadioGroupExample { options = signal([ @@ -559,12 +559,12 @@ class RadioGroupExample { @Component({ template: ` -
-
0
-
1
-
2
+
+
0
+
1
+
2
`, - imports: [CdkRadioGroup, CdkRadioButton], + imports: [RadioGroup, RadioButton], }) class DefaultRadioGroupExample {} diff --git a/src/cdk-experimental/radio-group/radio-group.ts b/src/aria/radio-group/radio-group.ts similarity index 78% rename from src/cdk-experimental/radio-group/radio-group.ts rename to src/aria/radio-group/radio-group.ts index 71c6052153cf..a832139e92ec 100644 --- a/src/cdk-experimental/radio-group/radio-group.ts +++ b/src/aria/radio-group/radio-group.ts @@ -26,10 +26,10 @@ import { RadioGroupPattern, ToolbarRadioGroupInputs, ToolbarRadioGroupPattern, -} from '../ui-patterns'; +} from '@angular/aria/ui-patterns'; import {Directionality} from '@angular/cdk/bidi'; import {_IdGenerator} from '@angular/cdk/a11y'; -import {CdkToolbarWidgetGroup} from '@angular/cdk-experimental/toolbar'; +import {ToolbarWidgetGroup} from '@angular/aria/toolbar'; // TODO: Move mapSignal to it's own file so it can be reused across components. @@ -70,23 +70,23 @@ export function mapSignal( * A radio button group container. * * Radio groups are used to group multiple radio buttons or radio group labels so they function as - * a single form control. The CdkRadioGroup is meant to be used in conjunction with CdkRadioButton + * a single form control. The RadioGroup is meant to be used in conjunction with RadioButton * as follows: * * ```html - *
- * - * - * + *
+ *
Option 1
+ *
Option 2
+ *
Option 3
*
* ``` */ @Directive({ - selector: '[cdkRadioGroup]', - exportAs: 'cdkRadioGroup', + selector: '[ngRadioGroup]', + exportAs: 'ngRadioGroup', host: { 'role': 'radiogroup', - 'class': 'cdk-radio-group', + 'class': 'ng-radio-group', '[attr.tabindex]': 'pattern.tabindex()', '[attr.aria-readonly]': 'pattern.readonly()', '[attr.aria-disabled]': 'pattern.disabled()', @@ -98,29 +98,29 @@ export function mapSignal( }, hostDirectives: [ { - directive: CdkToolbarWidgetGroup, + directive: ToolbarWidgetGroup, inputs: ['disabled'], }, ], }) -export class CdkRadioGroup { +export class RadioGroup { /** A reference to the radio group element. */ private readonly _elementRef = inject(ElementRef); - /** A reference to the CdkToolbarWidgetGroup, if the radio group is in a toolbar. */ - private readonly _cdkToolbarWidgetGroup = inject(CdkToolbarWidgetGroup); + /** A reference to the ToolbarWidgetGroup, if the radio group is in a toolbar. */ + private readonly _toolbarWidgetGroup = inject(ToolbarWidgetGroup); - /** Whether the radio group is inside of a CdkToolbar. */ - private readonly _hasToolbar = computed(() => !!this._cdkToolbarWidgetGroup.toolbar()); + /** Whether the radio group is inside of a Toolbar. */ + private readonly _hasToolbar = computed(() => !!this._toolbarWidgetGroup.toolbar()); - /** The CdkRadioButtons nested inside of the CdkRadioGroup. */ - private readonly _cdkRadioButtons = contentChildren(CdkRadioButton, {descendants: true}); + /** The RadioButtons nested inside of the RadioGroup. */ + private readonly _radioButtons = contentChildren(RadioButton, {descendants: true}); /** A signal wrapper for directionality. */ protected textDirection = inject(Directionality).valueSignal; - /** The RadioButton UIPatterns of the child CdkRadioButtons. */ - protected items = computed(() => this._cdkRadioButtons().map(radio => radio.pattern)); + /** The RadioButton UIPatterns of the child RadioButtons. */ + protected items = computed(() => this._radioButtons().map(radio => radio.pattern)); /** Whether the radio group is vertically or horizontally oriented. */ readonly orientation = input<'vertical' | 'horizontal'>('vertical'); @@ -167,7 +167,7 @@ export class CdkRadioGroup { const element = e.target.closest('[role="radio"]'); return this.items().find(i => i.element() === element); }, - toolbar: this._cdkToolbarWidgetGroup.toolbar, + toolbar: this._toolbarWidgetGroup.toolbar, }; this.pattern = this._hasToolbar() @@ -175,7 +175,7 @@ export class CdkRadioGroup { : new RadioGroupPattern(inputs as RadioGroupInputs); if (this._hasToolbar()) { - this._cdkToolbarWidgetGroup.controls.set(this.pattern as ToolbarRadioGroupPattern); + this._toolbarWidgetGroup.controls.set(this.pattern as ToolbarRadioGroupPattern); } afterRenderEffect(() => { @@ -199,29 +199,29 @@ export class CdkRadioGroup { } } -/** A selectable radio button in a CdkRadioGroup. */ +/** A selectable radio button in a RadioGroup. */ @Directive({ - selector: '[cdkRadioButton]', - exportAs: 'cdkRadioButton', + selector: '[ngRadioButton]', + exportAs: 'ngRadioButton', host: { 'role': 'radio', - 'class': 'cdk-radio-button', - '[class.cdk-active]': 'pattern.active()', + 'class': 'ng-radio-button', + '[attr.data-active]': 'pattern.active()', '[attr.tabindex]': 'pattern.tabindex()', '[attr.aria-checked]': 'pattern.selected()', '[attr.aria-disabled]': 'pattern.disabled()', '[id]': 'pattern.id()', }, }) -export class CdkRadioButton { +export class RadioButton { /** A reference to the radio button element. */ private readonly _elementRef = inject(ElementRef); - /** The parent CdkRadioGroup. */ - private readonly _cdkRadioGroup = inject(CdkRadioGroup); + /** The parent RadioGroup. */ + private readonly _radioGroup = inject(RadioGroup); /** A unique identifier for the radio button. */ - private readonly _generatedId = inject(_IdGenerator).getId('cdk-radio-button-'); + private readonly _generatedId = inject(_IdGenerator).getId('ng-radio-button-'); /** A unique identifier for the radio button. */ readonly id = computed(() => this._generatedId); @@ -230,7 +230,7 @@ export class CdkRadioButton { readonly value = input.required(); /** The parent RadioGroup UIPattern. */ - readonly group = computed(() => this._cdkRadioGroup.pattern); + readonly group = computed(() => this._radioGroup.pattern); /** A reference to the radio button element to be focused on navigation. */ element = computed(() => this._elementRef.nativeElement); diff --git a/src/cdk-experimental/tabs/BUILD.bazel b/src/aria/tabs/BUILD.bazel similarity index 84% rename from src/cdk-experimental/tabs/BUILD.bazel rename to src/aria/tabs/BUILD.bazel index dd0315226401..8fbfc69b87c0 100644 --- a/src/cdk-experimental/tabs/BUILD.bazel +++ b/src/aria/tabs/BUILD.bazel @@ -6,13 +6,12 @@ ng_project( name = "tabs", srcs = [ "index.ts", - "public-api.ts", "tabs.ts", ], deps = [ "//:node_modules/@angular/core", - "//src/cdk-experimental/deferred-content", - "//src/cdk-experimental/ui-patterns", + "//src/aria/deferred-content", + "//src/aria/ui-patterns", "//src/cdk/a11y", "//src/cdk/bidi", ], diff --git a/src/cdk-experimental/radio-group/index.ts b/src/aria/tabs/index.ts similarity index 75% rename from src/cdk-experimental/radio-group/index.ts rename to src/aria/tabs/index.ts index 52b3c7a5156f..7e81afdec8d6 100644 --- a/src/cdk-experimental/radio-group/index.ts +++ b/src/aria/tabs/index.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.dev/license */ -export * from './public-api'; +export {Tabs, TabList, Tab, TabPanel, TabContent} from './tabs'; diff --git a/src/cdk-experimental/tabs/tabs.spec.ts b/src/aria/tabs/tabs.spec.ts similarity index 97% rename from src/cdk-experimental/tabs/tabs.spec.ts rename to src/aria/tabs/tabs.spec.ts index e341089fa9ac..d7a7d7367a34 100644 --- a/src/cdk-experimental/tabs/tabs.spec.ts +++ b/src/aria/tabs/tabs.spec.ts @@ -3,7 +3,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {Direction} from '@angular/cdk/bidi'; import {provideFakeDirectionality, runAccessibilityChecks} from '@angular/cdk/testing/private'; -import {CdkTabs, CdkTabList, CdkTab, CdkTabPanel, CdkTabContent} from './tabs'; +import {Tabs, TabList, Tab, TabPanel, TabContent} from './tabs'; interface ModifierKeys { ctrlKey?: boolean; @@ -19,7 +19,7 @@ interface TestTabDefinition { disabled?: boolean; } -describe('CdkTabs', () => { +describe('Tabs', () => { let fixture: ComponentFixture; let testComponent: TestTabsComponent; @@ -98,10 +98,10 @@ describe('CdkTabs', () => { } function defineTestVariables() { - tabsDebugElement = fixture.debugElement.query(By.directive(CdkTabs)); - tabListDebugElement = fixture.debugElement.query(By.directive(CdkTabList)); - tabDebugElements = fixture.debugElement.queryAll(By.directive(CdkTab)); - tabPanelDebugElements = fixture.debugElement.queryAll(By.directive(CdkTabPanel)); + tabsDebugElement = fixture.debugElement.query(By.directive(Tabs)); + tabListDebugElement = fixture.debugElement.query(By.directive(TabList)); + tabDebugElements = fixture.debugElement.queryAll(By.directive(Tab)); + tabPanelDebugElements = fixture.debugElement.queryAll(By.directive(TabPanel)); tabsElement = tabsDebugElement.nativeElement; tabListElement = tabListDebugElement.nativeElement; @@ -135,7 +135,7 @@ describe('CdkTabs', () => { }); }); - describe('CdkTabList', () => { + describe('TabList', () => { it('should have role="tablist"', () => { expect(tabListElement.getAttribute('role')).toBe('tablist'); }); @@ -171,7 +171,7 @@ describe('CdkTabs', () => { }); }); - describe('CdkTab', () => { + describe('Tab', () => { it('should have role="tab"', () => { tabElements.forEach(tabElement => { expect(tabElement.getAttribute('role')).toBe('tab'); @@ -215,7 +215,7 @@ describe('CdkTabs', () => { }); }); - describe('CdkTabPanel', () => { + describe('TabPanel', () => { it('should have role="tabpanel"', () => { tabPanelElements.forEach(panelElement => { expect(panelElement.getAttribute('role')).toBe('tabpanel'); @@ -674,9 +674,9 @@ describe('CdkTabs', () => { @Component({ template: ` -
-