From 4c02b1e786ad9f16f197eeab69cf82d721d75f30 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Wed, 15 Apr 2026 15:27:49 +0300 Subject: [PATCH] feat!: remove deprecated APIs - Remove CascadeController and @cascades decorator (use @lit/context) - Remove SlotController.anonymous (use SlotController.default) - Remove pf-popover closeButtonLabel / close-label attribute (use accessible-close-label) pfeCustomElementsManifestConfig was already removed from the codebase. Closes #2975, closes #2974, closes #2973, closes #2972 Assisted-By: Claude Opus 4.6 (1M context) --- .changeset/remove-deprecated-apis.md | 13 ++ .../controllers/cascade-controller.ts | 169 ------------------ .../controllers/slot-controller-server.ts | 2 - core/pfe-core/controllers/slot-controller.ts | 2 - core/pfe-core/decorators.ts | 1 - core/pfe-core/decorators/cascades.ts | 23 --- core/pfe-core/package.json | 2 - elements/pf-popover/pf-popover.ts | 11 +- 8 files changed, 15 insertions(+), 208 deletions(-) create mode 100644 .changeset/remove-deprecated-apis.md delete mode 100644 core/pfe-core/controllers/cascade-controller.ts delete mode 100644 core/pfe-core/decorators/cascades.ts diff --git a/.changeset/remove-deprecated-apis.md b/.changeset/remove-deprecated-apis.md new file mode 100644 index 0000000000..341e191b2f --- /dev/null +++ b/.changeset/remove-deprecated-apis.md @@ -0,0 +1,13 @@ +--- +"@patternfly/pfe-core": major +"@patternfly/elements": major +--- + +Removed deprecated APIs: + +- **`CascadeController`** and **`@cascades`** decorator are removed. + Use [`@lit/context`](https://lit.dev/docs/data/context/) instead for + sharing data between parent and child elements. +- **`SlotController.anonymous`** is removed. Use `SlotController.default` instead. +- **`pf-popover`**: the `close-label` attribute and `closeButtonLabel` property + are removed. Use `accessible-close-label` instead. diff --git a/core/pfe-core/controllers/cascade-controller.ts b/core/pfe-core/controllers/cascade-controller.ts deleted file mode 100644 index bde105cb07..0000000000 --- a/core/pfe-core/controllers/cascade-controller.ts +++ /dev/null @@ -1,169 +0,0 @@ -import type { ReactiveController, ReactiveElement } from 'lit'; - -import { bound } from '../decorators/bound.js'; -import { debounce } from '../functions/debounce.js'; -import { Logger } from './logger.js'; - -/** - * @deprecated use context, especially via `@patternfly/pfe-core/functions/context.js`; - */ -export interface Options { - properties: Partial>; - prefix?: string; -} - -/** - * @deprecated use context, especially via `@patternfly/pfe-core/functions/context.js`; - */ -export class CascadeController implements ReactiveController { - private class: typeof ReactiveElement; - - private logger: Logger; - - static instances: WeakMap> = - new WeakMap>(); - - mo: MutationObserver = new MutationObserver(this.parse); - - cache: Map = new Map(); - - constructor(public host: E, public options?: Options | undefined) { - this.class = host.constructor as typeof ReactiveElement; - this.logger = new Logger(this.host); - CascadeController.instances.set(host, this); - const properties = this.options?.properties ?? {} as Options['properties']; - for (const [propName, cascade] of Object.entries(properties)) { - this.initProp(propName, cascade); - } - host.addController(this); - this.cascadeProperties = debounce(this.cascadeProperties, 1); - } - - hostUpdated(): void { - this.cascadeProperties(); - } - - hostConnected(): void { - this.mo.observe(this.host, { attributes: true, childList: true }); - this.cascadeProperties(); - } - - hostDisconnected(): void { - this.mo.disconnect(); - } - - /** - * Handles the cascading of properties to nested components when new elements are added - * Attribute updates/additions are handled by the attribute callback - * @param [nodeList=this.host.children] - */ - cascadeProperties(nodeList: HTMLCollection | NodeList = this.host.children): void { - if (this.host.isConnected) { - const selectors = this.cache.keys(); - - // Find out if anything in the nodeList matches any of the observed selectors for cacading properties - if (!nodeList) { - return this._cascadeAttributes(selectors, this.cache); - } - - - for (const node of nodeList) { - // if this node has a match function (i.e., it's an HTMLElement, not a text node), - if (node instanceof Element) { - // see if it matches one of the selectors, otherwise drop it (like it's hot). - for (const selector of selectors) { - // console.log('_copyAttribute', name, value, el.getAttribute(name)); - if (node.matches(selector)) { - const attrNames = this.cache.get(selector); - // each selector can match multiple properties/attributes, so - // copy each of them - for (const attrName of attrNames ?? []) { - this._copyAttribute(attrName, node); - } - } - } - } - } - } - } - - /** - * Gets the configured attribute name for the decorated property, - * falling back to the lowercased property name, and caches the attribute name - * with it's designated child selectors for value-propagation on change - * @param propName - * @param cascade - */ - initProp(propName: string, cascade: string | string[]): void { - for (const nodeItem of [cascade].flat(Infinity).filter(Boolean) as string[]) { - const { attribute } = this.class.getPropertyOptions(propName); - - const attr = - typeof attribute === 'string' ? attribute - : propName.toLowerCase(); - - // Create an object with the node as the key and an array of attributes - // that are to be cascaded down to it - if (!this.cache.get(nodeItem)) { - this.cache.set(nodeItem, [attr]); - } else { - this.cache.get(nodeItem)?.push(attr); - } - } - } - - @bound private parse(mutations: MutationRecord[]) { - // Iterate over the mutation list, look for cascade updates - for (const mutation of mutations ?? []) { - // If a new node is added, attempt to cascade attributes to it - if (mutation.type === 'childList' && mutation.addedNodes.length) { - this.cascadeProperties(mutation.addedNodes); - } else if (mutation.type === 'attributes') { - this._cascadeAttributes(this.cache.keys(), this.cache); - } - } - } - - /** - * Copy the named attribute to a target element. - * @param name attr name - * @param el element - */ - private async _copyAttribute(name: string, el: Element) { - this.logger.log(`copying ${name} to ${el}`); - const value = this.host.getAttribute(name); - if (el.isConnected) { - if (value == null) { - el.removeAttribute(name); - } else { - el.setAttribute(name, value); - } - } - } - - private _cascadeAttributes(selectors: IterableIterator, set: this['cache']) { - for (const selector of selectors) { - for (const attr of set.get(selector) ?? []) { - this._cascadeAttribute(attr, selector); - } - } - } - - /** - * Trigger a cascade of the named attribute to any child elements that match - * the `to` selector. The selector can match elements in the light DOM and - * shadow DOM. - * @param name The name of the attribute to cascade (not necessarily the same as the property name). - * @param to A CSS selector that matches the elements that should received the cascaded attribute. The selector will be applied within `this` element's light and shadow DOM trees. - */ - private _cascadeAttribute(name: string, to: string) { - const recipients = [ - ...this.host.querySelectorAll(to), - ...this.host.shadowRoot?.querySelectorAll(to) ?? [], - ]; - - for (const node of recipients) { - this._copyAttribute(name, node); - } - } -} diff --git a/core/pfe-core/controllers/slot-controller-server.ts b/core/pfe-core/controllers/slot-controller-server.ts index 40b7ea649b..950a732109 100644 --- a/core/pfe-core/controllers/slot-controller-server.ts +++ b/core/pfe-core/controllers/slot-controller-server.ts @@ -7,8 +7,6 @@ import { export class SlotController implements SlotControllerPublicAPI { public static default = Symbol('default slot') satisfies symbol as symbol; - /** @deprecated use `default` */ - public static anonymous: symbol = this.default; static attribute = 'ssr-hint-has-slotted' as const; diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index 85cb4d962e..4c2d6961da 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -130,8 +130,6 @@ class SlotRecord { export class SlotController implements SlotControllerPublicAPI { public static default = Symbol('default slot') satisfies symbol as symbol; - /** @deprecated use `default` */ - public static anonymous: symbol = this.default; #slotRecords = new Map(); diff --git a/core/pfe-core/decorators.ts b/core/pfe-core/decorators.ts index 17bdfe6736..2acab8fb3e 100644 --- a/core/pfe-core/decorators.ts +++ b/core/pfe-core/decorators.ts @@ -1,5 +1,4 @@ export * from './decorators/bound.js'; -export * from './decorators/cascades.js'; export * from './decorators/deprecation.js'; export * from './decorators/initializer.js'; export * from './decorators/listen.js'; diff --git a/core/pfe-core/decorators/cascades.ts b/core/pfe-core/decorators/cascades.ts deleted file mode 100644 index 84134434c3..0000000000 --- a/core/pfe-core/decorators/cascades.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ReactiveElement } from 'lit'; - -import { CascadeController } from '../controllers/cascade-controller.js'; - -/** - * Cascades the decorated attribute to children - * @param items - * @deprecated use context, especially via `@patternfly/pfe-core/functions/context.js`; - */ -export function cascades(...items: string[]): PropertyDecorator { - return function(proto: T, key: string & keyof T) { - (proto.constructor as typeof ReactiveElement).addInitializer(x => { - const instance = x as ReactiveElement; - // You can have multiple `@cascades` decorators on an element - // and it will only get one CascadeController for all of them - if (!CascadeController.instances.has(instance)) { - CascadeController.instances.set(instance, new CascadeController(instance)); - } - - CascadeController.instances.get(instance)?.initProp(key, items); - }); - } as PropertyDecorator; -} diff --git a/core/pfe-core/package.json b/core/pfe-core/package.json index e165afc3bc..a7a2c3703a 100644 --- a/core/pfe-core/package.json +++ b/core/pfe-core/package.json @@ -22,7 +22,6 @@ "./core.js": "./core.js", "./decorators.js": "./decorators.js", "./controllers/activedescendant-controller.js": "./controllers/activedescendant-controller.js", - "./controllers/cascade-controller.js": "./controllers/cascade-controller.js", "./controllers/css-variable-controller.js": "./controllers/css-variable-controller.js", "./controllers/floating-dom-controller.js": "./controllers/floating-dom-controller.js", "./controllers/internals-controller.js": "./controllers/internals-controller.js", @@ -41,7 +40,6 @@ "./controllers/timestamp-controller.js": "./controllers/timestamp-controller.js", "./controllers/tabs-controller.js": "./controllers/tabs-controller.js", "./decorators/bound.js": "./decorators/bound.js", - "./decorators/cascades.js": "./decorators/cascades.js", "./decorators/deprecation.js": "./decorators/deprecation.js", "./decorators/initializer.js": "./decorators/initializer.js", "./decorators/observed.js": "./decorators/observed.js", diff --git a/elements/pf-popover/pf-popover.ts b/elements/pf-popover/pf-popover.ts index d459d46452..f2297bdc05 100644 --- a/elements/pf-popover/pf-popover.ts +++ b/elements/pf-popover/pf-popover.ts @@ -9,7 +9,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { FloatingDOMController } from '@patternfly/pfe-core/controllers/floating-dom-controller.js'; import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js'; -import { deprecation } from '@patternfly/pfe-core/decorators/deprecation.js'; + import { bound } from '@patternfly/pfe-core/decorators/bound.js'; import { ComposedEvent, StringListConverter } from '@patternfly/pfe-core/core.js'; @@ -161,13 +161,6 @@ export class PfPopover extends LitElement { */ @property({ reflect: true, attribute: 'accessible-close-label' }) accessibleCloseLabel?: string; - /** - * @deprecated do not use the color-palette attribute, which was added by mistake. use context-providing containers (e.g. rh-card) instead - */ - @deprecation({ - alias: 'accessible-close-label', - attribute: 'close-label', - }) closeButtonLabel?: string; /** * The text announced by the screen reader to indicate the popover's severity. @@ -268,7 +261,7 @@ export class PfPopover extends LitElement {