From 45c50468866d273608eca78dc739489c8732952a Mon Sep 17 00:00:00 2001 From: Pawan Kumar Date: Tue, 16 Dec 2025 14:26:51 +0530 Subject: [PATCH 01/10] fix: add missing ids to components --- .../assets/css/src/scss/_utils/_tokens.scss | 2 +- packages/ui/accordion/src/accordion-item.tsx | 37 ++++++++++++++----- packages/ui/accordion/src/accordion.types.ts | 1 + .../advanced-banner/src/advanced-banner.tsx | 6 ++- .../src/advanced-banner.types.ts | 4 ++ packages/ui/alert-banner/src/alert-banner.tsx | 6 ++- .../ui/alert-banner/src/alert-banner.types.ts | 4 ++ packages/ui/avatar/src/avatar.tsx | 7 +++- packages/ui/avatar/src/avatar.types.ts | 4 ++ packages/ui/basic-box/src/basic-box.tsx | 35 ++++++++++++++---- packages/ui/basic-box/src/basic-box.types.ts | 4 ++ packages/ui/box/src/box-group.tsx | 14 ++++++- packages/ui/box/src/box-group.types.ts | 4 ++ packages/ui/box/src/box.tsx | 28 ++++++++++---- packages/ui/box/src/box.types.ts | 4 ++ packages/ui/builder/src/builder-button.tsx | 6 ++- packages/ui/builder/src/builder-empty.tsx | 6 ++- packages/ui/builder/src/builder-field.tsx | 11 +++++- packages/ui/builder/src/builder-wrapper.tsx | 6 ++- packages/ui/builder/src/builder.tsx | 6 ++- packages/ui/builder/src/builder.types.ts | 20 ++++++++++ packages/ui/checkbox/src/checkbox-group.tsx | 6 +-- packages/ui/checkbox/src/checkbox-wrapper.tsx | 7 +++- packages/ui/checkbox/src/checkbox.tsx | 20 +++++----- packages/ui/checkbox/src/checkbox.types.ts | 4 ++ packages/ui/code-editor/src/code-editor.tsx | 6 ++- .../ui/code-editor/src/code-editor.types.ts | 4 ++ packages/ui/code-snippet/src/code-snippet.tsx | 6 ++- .../ui/code-snippet/src/code-snippet.types.ts | 4 ++ .../ui/color-picker/src/color-picker.types.ts | 8 ++++ .../config-table/src/config-table-details.tsx | 7 +++- packages/ui/config-table/src/config-table.tsx | 11 +++++- .../ui/config-table/src/config-table.types.ts | 8 ++++ .../dashboard-widget/src/dashboard-widget.tsx | 6 ++- .../src/dashboard-widget.types.ts | 4 ++ packages/ui/date-picker/src/date-picker.tsx | 7 +--- packages/ui/drawer/src/drawer-body.tsx | 11 +++++- packages/ui/drawer/src/drawer-footer.tsx | 11 +++++- packages/ui/drawer/src/drawer-header.tsx | 7 +++- packages/ui/drawer/src/drawer.tsx | 5 +++ packages/ui/drawer/src/drawer.types.ts | 12 ++++++ .../ui/dropdown/src/dropdown-menu-item.tsx | 8 ++-- packages/ui/dropdown/src/dropdown-menu.tsx | 13 +++++-- packages/ui/dropdown/src/dropdown.tsx | 28 +++++++++++--- packages/ui/dropdown/src/dropdown.types.ts | 12 ++++++ .../src/editor-toolbar-field.tsx | 7 +++- .../ui/editor-toolbar/src/editor-toolbar.tsx | 6 ++- .../src/editor-toolbar.types.ts | 8 ++++ packages/ui/empty-state/src/empty-state.tsx | 11 +++++- .../ui/empty-state/src/empty-state.types.ts | 4 ++ .../ui/field-list/src/field-list-item.tsx | 16 ++++++-- packages/ui/footer/src/footer.tsx | 6 ++- packages/ui/footer/src/footer.types.ts | 4 ++ .../form-field/src/elements/error-message.tsx | 2 +- .../ui/form-field/src/elements/helper.tsx | 2 +- packages/ui/form-field/src/elements/label.tsx | 2 +- packages/ui/form-field/src/form-field.tsx | 6 +-- packages/ui/grid/src/elements/col.tsx | 12 +++++- packages/ui/grid/src/elements/row.tsx | 7 +++- packages/ui/grid/src/grid.types.ts | 8 ++++ packages/ui/icon/src/icon.tsx | 5 +++ packages/ui/icon/src/icon.types.ts | 4 ++ packages/ui/input/src/input.tsx | 16 +++++++- packages/ui/integration/src/integration.tsx | 6 ++- .../ui/integration/src/integration.types.ts | 4 ++ packages/ui/link/src/link.tsx | 7 +++- packages/ui/link/src/link.types.ts | 4 ++ packages/ui/modal/src/modal-body.tsx | 6 ++- packages/ui/modal/src/modal-footer.tsx | 6 ++- packages/ui/modal/src/modal-header.tsx | 6 ++- packages/ui/modal/src/modal.tsx | 7 +++- packages/ui/modal/src/modal.types.ts | 3 ++ .../src/navigation-wpmudev.types.ts | 24 ++++++++++++ packages/ui/navigation/src/navigation.tsx | 6 ++- .../ui/navigation/src/navigation.types.ts | 12 ++++++ .../src/notification-renderer.tsx | 7 +++- packages/ui/notification/src/notification.tsx | 10 ++--- .../ui/notification/src/notification.types.ts | 4 ++ packages/ui/page-header/src/page-header.tsx | 7 +++- .../ui/page-header/src/page-header.types.ts | 4 ++ packages/ui/pagination/src/pagination.tsx | 6 ++- .../ui/pagination/src/pagination.types.ts | 8 ++++ packages/ui/popover/src/popover.tsx | 5 +++ packages/ui/popover/src/popover.types.ts | 4 ++ packages/ui/progress-bar/src/progress-bar.tsx | 6 ++- .../ui/progress-bar/src/progress-bar.types.ts | 4 ++ packages/ui/radio/src/radio-group.tsx | 6 ++- packages/ui/radio/src/radio.tsx | 24 +++++------- packages/ui/radio/src/radio.types.ts | 4 ++ .../ui/recipient/src/recipient-button.tsx | 7 +++- packages/ui/recipient/src/recipient-email.tsx | 7 +++- packages/ui/recipient/src/recipient-name.tsx | 7 +++- packages/ui/recipient/src/recipient.tsx | 6 ++- packages/ui/recipient/src/recipient.type.ts | 4 ++ .../rich-text-editor/src/rich-text-editor.tsx | 12 ++++-- packages/ui/score/src/score.tsx | 6 ++- packages/ui/score/src/score.types.ts | 4 ++ packages/ui/search/src/search-option-item.tsx | 6 ++- packages/ui/search/src/search-options.tsx | 7 +++- packages/ui/search/src/search.tsx | 2 +- packages/ui/search/src/search.types.ts | 8 ++++ .../src/segmented-control-button.tsx | 7 ++-- .../src/segmented-control.types.ts | 2 + packages/ui/select/src/select.types.ts | 8 ++++ packages/ui/selector/src/selector.tsx | 14 +++++-- packages/ui/selector/src/selector.types.ts | 6 +++ .../ui/setting-block/src/setting-block.tsx | 6 ++- .../setting-block/src/setting-block.types.ts | 8 ++++ packages/ui/setup-banner/src/setup-banner.tsx | 6 ++- .../ui/setup-banner/src/setup-banner.types.ts | 4 ++ packages/ui/sidebar/src/sidebar-dropdown.tsx | 2 +- packages/ui/sidebar/src/sidebar-item.tsx | 7 +++- packages/ui/sidebar/src/sidebar.types.ts | 4 ++ packages/ui/skeleton/src/skeleton-base.tsx | 11 +++++- packages/ui/skeleton/src/skeleton.types.ts | 3 ++ packages/ui/spinner/src/spinner-hook.tsx | 2 +- packages/ui/spinner/src/spinner.tsx | 6 ++- packages/ui/spinner/src/spinner.types.ts | 8 ++++ .../ui/summary-box/src/summary-box-body.tsx | 7 +++- .../summary-box/src/summary-box-content.tsx | 7 +++- .../ui/summary-box/src/summary-box-footer.tsx | 11 +++++- .../ui/summary-box/src/summary-box-item.tsx | 11 +++++- .../ui/summary-box/src/summary-box-row.tsx | 7 +++- packages/ui/summary-box/src/summary-box.tsx | 6 ++- .../ui/summary-box/src/summary-box.types.ts | 24 ++++++++++++ packages/ui/table/src/table-row.tsx | 6 +-- packages/ui/table/src/table-toolbar.tsx | 8 ++-- packages/ui/table/src/table.types.ts | 4 ++ packages/ui/tabs/src/tabs.tsx | 6 ++- packages/ui/tabs/src/tabs.types.ts | 2 + packages/ui/tag/src/tag.tsx | 7 +++- packages/ui/tag/src/tag.types.ts | 4 ++ packages/ui/toggle/src/toggle.tsx | 27 ++++++++++---- packages/ui/tooltip/src/tooltip.tsx | 8 ++-- packages/ui/tooltip/src/tooltip.types.ts | 4 ++ packages/ui/tree-view/src/tree-view-info.tsx | 4 +- packages/ui/tree-view/src/tree-view-item.tsx | 2 +- packages/ui/uploader/src/uploader-button.tsx | 7 +++- packages/ui/uploader/src/uploader.tsx | 15 +++++--- packages/ui/uploader/src/uploader.types.ts | 4 ++ .../ui/upsell-notice/src/upsell-notice.tsx | 6 ++- .../upsell-notice/src/upsell-notice.types.ts | 4 ++ packages/ui/upsell/src/upsell.tsx | 6 ++- packages/ui/upsell/src/upsell.types.ts | 4 ++ 144 files changed, 921 insertions(+), 206 deletions(-) diff --git a/packages/assets/css/src/scss/_utils/_tokens.scss b/packages/assets/css/src/scss/_utils/_tokens.scss index a7da68e17..e6315dc3e 100644 --- a/packages/assets/css/src/scss/_utils/_tokens.scss +++ b/packages/assets/css/src/scss/_utils/_tokens.scss @@ -1,6 +1,6 @@ // Do not edit directly -// Generated on Mon, 03 Nov 2025 10:48:23 GMT +// Generated on Tue, 16 Dec 2025 08:02:53 GMT $accordion-border-radius-sm: 10px; $advanced-banner-background: linear-gradient(90deg, #222 0%, #383323 48.96%, #514524 100%); diff --git a/packages/ui/accordion/src/accordion-item.tsx b/packages/ui/accordion/src/accordion-item.tsx index 0870c0b1b..da6a0902b 100644 --- a/packages/ui/accordion/src/accordion-item.tsx +++ b/packages/ui/accordion/src/accordion-item.tsx @@ -17,6 +17,7 @@ import { AccordionItemProps } from "./accordion.types" // The AccordionItem component is defined as a functional component using React.FC. const AccordionItem: React.FC = ({ + id, title = "{title}", description, children, @@ -40,7 +41,8 @@ const AccordionItem: React.FC = ({ const [isPressed, setIsPressed] = useState(false) // Custom hook to generate a unique ID for the accordion item. - const uniqueId = useId() + const generatedId = useId() + const uniqueId = id || `sui_accordion_${generatedId}` // Get the "toggle" method and "isCurrentlyExpanded" state from the current AccordionItem const { toggle, isCurrentlyExpanded } = useAccordion({ @@ -50,8 +52,8 @@ const AccordionItem: React.FC = ({ const { spacing } = useContext(AccordionContext) // IDs for the accordion and its panel to manage accessibility. - const accordionId = `sui-accordion-${uniqueId}` - const accordionPanelId = `sui-accordion-panel-${uniqueId}` + const accordionId = uniqueId + const accordionPanelId = `${uniqueId}_panel` const onMouseDownCapture = () => { setIsPressed(true) @@ -150,13 +152,19 @@ const AccordionItem: React.FC = ({ )} {/* Content of the accordion item's header */} -
+
{(!!hasCheckbox || !!icon) && ( -
+
{hasCheckbox && ( = ({ {!!icon && icon}
)} -
+

{title}

{!isEmpty(description ?? "") &&

{description}

}
{/* Icon component to display a chevron icon */} -
+
@@ -188,7 +202,12 @@ const AccordionItem: React.FC = ({ })} data-testid="accordion-item-panel" > -
{children}
+
+ {children} +
) diff --git a/packages/ui/accordion/src/accordion.types.ts b/packages/ui/accordion/src/accordion.types.ts index 6508eac27..42522ad7e 100644 --- a/packages/ui/accordion/src/accordion.types.ts +++ b/packages/ui/accordion/src/accordion.types.ts @@ -36,6 +36,7 @@ type AccordionCheckboxProps = interface AccordionItemBaseProps extends SuiHTMLAttributes>, SuiStyleType { + id?: string // Optional custom ID for the accordion item. title?: string // The title of the accordion item. description?: string // The description of the accordion item. children?: React.ReactNode // The content of the accordion item, which can be any valid React node. diff --git a/packages/ui/advanced-banner/src/advanced-banner.tsx b/packages/ui/advanced-banner/src/advanced-banner.tsx index 980bbcc3b..5f042f25f 100644 --- a/packages/ui/advanced-banner/src/advanced-banner.tsx +++ b/packages/ui/advanced-banner/src/advanced-banner.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useId } from "react" import { _renderHTMLPropsSafely, generateCN, isEmpty } from "@wpmudev/sui-utils" import { Button, ButtonProps } from "@wpmudev/sui-button" @@ -7,6 +7,7 @@ import { useStyles } from "@wpmudev/sui-hooks" // Build "advanced-banner" component const AdvancedBanner: React.FC = ({ + id, variation = "plugin", imageUrl = "https://placehold.co/100", title = "Banner Title", @@ -23,6 +24,8 @@ const AdvancedBanner: React.FC = ({ _htmlProps = {}, _style = {}, }) => { + const generatedId = useId() + const advancedBannerId = id || `sui_advanced_banner_${generatedId}` const { suiInlineClassname } = useStyles(_style, className) // Define class name @@ -43,6 +46,7 @@ const AdvancedBanner: React.FC = ({ return (
>, SuiStyleType { + /** + * Unique identifier for the advanced banner. + */ + id?: string /** * Sets the headline text */ diff --git a/packages/ui/alert-banner/src/alert-banner.tsx b/packages/ui/alert-banner/src/alert-banner.tsx index 6b821bf29..347605717 100644 --- a/packages/ui/alert-banner/src/alert-banner.tsx +++ b/packages/ui/alert-banner/src/alert-banner.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from "react" +import React, { useId, useState, useCallback } from "react" import { _renderHTMLPropsSafely, generateCN, isEmpty } from "@wpmudev/sui-utils" import { Button, ButtonProps } from "@wpmudev/sui-button" import Icons from "@wpmudev/sui-icons" @@ -7,6 +7,7 @@ import { useDefaultChildren, useStyles } from "@wpmudev/sui-hooks" import { AlertBannerProps } from "./alert-banner.types" const AlertBanner: React.FC = ({ + id, children, variation = "informative", actions, @@ -18,6 +19,8 @@ const AlertBanner: React.FC = ({ _style = {}, _htmlProps, }) => { + const generatedId = useId() + const alertBannerId = id || `sui_alert_banner_${generatedId}` // State to control the visibility of the alert banner const [isVisible, setIsVisible] = useState(true) @@ -85,6 +88,7 @@ const AlertBanner: React.FC = ({ return (
> { + /** + * Unique identifier for the alert banner. + */ + id?: string /** * Alert Banner content */ diff --git a/packages/ui/avatar/src/avatar.tsx b/packages/ui/avatar/src/avatar.tsx index 834d96400..e6b01c14d 100644 --- a/packages/ui/avatar/src/avatar.tsx +++ b/packages/ui/avatar/src/avatar.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useId } from "react" import { _renderHTMLPropsSafely, generateCN, @@ -16,6 +16,7 @@ import { useStyles } from "@wpmudev/sui-hooks" // Build "avatar" component const Avatar: React.FC = ({ + id, image, status = "none", isSmall = false, @@ -25,6 +26,9 @@ const Avatar: React.FC = ({ _style = {}, onClick, }) => { + const generatedId = useId() + const avatarId = id || `sui_avatar_${generatedId}` + // Define image object const imageObj = Object.assign( { @@ -51,6 +55,7 @@ const Avatar: React.FC = ({ ) const attributes = { + id: avatarId, className: classNames, ..._renderHTMLPropsSafely(_htmlProps), "data-testid": "avatar", diff --git a/packages/ui/avatar/src/avatar.types.ts b/packages/ui/avatar/src/avatar.types.ts index 35ebba1aa..22b057fe7 100644 --- a/packages/ui/avatar/src/avatar.types.ts +++ b/packages/ui/avatar/src/avatar.types.ts @@ -9,6 +9,10 @@ import { IconsNamesType } from "@wpmudev/sui-icons" interface AvatarProps extends SuiHTMLAttributes>, SuiStyleType { + /** + * Unique identifier for the avatar. + */ + id?: string /** * The image source for the avatar. */ diff --git a/packages/ui/basic-box/src/basic-box.tsx b/packages/ui/basic-box/src/basic-box.tsx index 3fca4f07a..7e57048bb 100644 --- a/packages/ui/basic-box/src/basic-box.tsx +++ b/packages/ui/basic-box/src/basic-box.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useId } from "react" import { _renderHTMLPropsSafely, generateCN } from "@wpmudev/sui-utils" import { @@ -11,6 +11,7 @@ import { BasicBoxProps } from "./basic-box.types" // Build "basic-box" component const BasicBox: React.FC = ({ + id, title = "Box Title", description, headerActions, @@ -24,6 +25,9 @@ const BasicBox: React.FC = ({ _htmlProps, _style, }) => { + const generatedId = useId() + const basicBoxId = id || `sui_basic_box_${generatedId}` + // Interaction methods const [isHovered, isFocused, methods] = useInteraction({}) @@ -47,22 +51,27 @@ const BasicBox: React.FC = ({ return (
-
-
+
+
{!!title && (

{title}

)} -
+
{headerActions && headerActions}
{!!description && ( @@ -75,18 +84,28 @@ const BasicBox: React.FC = ({ )}
-
+
{children}
{!!footerActions && ( -
+
{!!footerActions?.[0] && ( -
+ )} {!!footerActions?.[1] && ( -
+ )} diff --git a/packages/ui/basic-box/src/basic-box.types.ts b/packages/ui/basic-box/src/basic-box.types.ts index 9ab489bb3..214addc2a 100644 --- a/packages/ui/basic-box/src/basic-box.types.ts +++ b/packages/ui/basic-box/src/basic-box.types.ts @@ -8,6 +8,10 @@ import { SuiHTMLAttributes, SuiStyleType } from "@wpmudev/sui-utils" interface BasicBoxProps extends SuiStyleType, SuiHTMLAttributes> { + /** + * The unique identifier for the basic box (optional). + */ + id?: string /** * The title of the BasicBox, which can contain React nodes. */ diff --git a/packages/ui/box/src/box-group.tsx b/packages/ui/box/src/box-group.tsx index 88dddc7e4..4c4ac98b0 100644 --- a/packages/ui/box/src/box-group.tsx +++ b/packages/ui/box/src/box-group.tsx @@ -1,4 +1,4 @@ -import React, { Children, Fragment, ReactElement } from "react" +import React, { Children, Fragment, ReactElement, useId } from "react" import { _renderHTMLPropsSafely, generateCN, @@ -10,6 +10,7 @@ import { useStyles } from "@wpmudev/sui-hooks" // Create box group component const BoxGroup: React.FC = ({ + id, isInline = true, children, className = "", @@ -19,16 +20,24 @@ const BoxGroup: React.FC = ({ _htmlProps = {}, _style = {}, }) => { + const generatedId = useId() + const boxGroupId = id || `sui_box_group_${generatedId}` + const { suiInlineClassname } = useStyles(_style, className) // Build content based in slots const slots = Children.map(children, (child, index) => { - const { slot, children: content } = (child as ReactElement)?.props ?? {} + const { + slot, + children: content, + id: childId, + } = (child as ReactElement)?.props ?? {} if (isObject(child) && ["left", "right"].includes(slot ?? "")) { return (
{content} @@ -53,6 +62,7 @@ const BoxGroup: React.FC = ({ return (
>, SuiStyleType { + /** + * The unique identifier for the box group (optional). + */ + id?: string /** * Indicates whether the boxes should be displayed inline or not. */ diff --git a/packages/ui/box/src/box.tsx b/packages/ui/box/src/box.tsx index 7fbf50433..528d06ece 100644 --- a/packages/ui/box/src/box.tsx +++ b/packages/ui/box/src/box.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useId } from "react" import { _renderHTMLPropsSafely, generateCN, @@ -14,6 +14,7 @@ import { useDefaultChildren, useStyles } from "@wpmudev/sui-hooks" // Create box component const Box: React.FC = ({ + id, title, description, icon, @@ -28,6 +29,9 @@ const Box: React.FC = ({ _htmlProps = {}, _style, }) => { + const generatedId = useId() + const boxId = id || `sui_box_${generatedId}` + // Prop(s) validation const hasTitle = !isUndefined(title) && !isEmpty(title) const hasIcon = !isUndefined(icon) && !isEmpty(icon) @@ -49,18 +53,20 @@ const Box: React.FC = ({ return (
{hasTitle && ( - -
-
+ +
+
{hasTitle && ( -
+
{hasIcon && IconTag && ( = ({ )} -

+

{title}

{hasLeft && headerLeft}
)} - {description &&

{description}

} + {description &&

{description}

}
-
+
{hasRight && headerRight}
diff --git a/packages/ui/box/src/box.types.ts b/packages/ui/box/src/box.types.ts index 41f0d0ed5..b0c72f375 100644 --- a/packages/ui/box/src/box.types.ts +++ b/packages/ui/box/src/box.types.ts @@ -9,6 +9,10 @@ import { SuiHTMLAttributes, SuiStyleType } from "@wpmudev/sui-utils" interface BoxProps extends SuiHTMLAttributes>, SuiStyleType { + /** + * The unique identifier for the box (optional). + */ + id?: string /** * The title of the box. */ diff --git a/packages/ui/builder/src/builder-button.tsx b/packages/ui/builder/src/builder-button.tsx index c0d23bdb5..c0280568e 100644 --- a/packages/ui/builder/src/builder-button.tsx +++ b/packages/ui/builder/src/builder-button.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback } from "react" +import React, { FC, useId, useCallback } from "react" import { generateCN, isEmpty } from "@wpmudev/sui-utils" import { useInteraction, useStyles } from "@wpmudev/sui-hooks" @@ -8,6 +8,7 @@ import { Button } from "@wpmudev/sui-button" import { BuilderButtonProps } from "./builder.types" const BuilderButton: FC = ({ + id, icon = "Add", title = "Insert new field", className, @@ -15,6 +16,8 @@ const BuilderButton: FC = ({ onClick = () => {}, _style = {}, }) => { + const generatedId = useId() + const builderButtonId = id || `sui_builder_button_${generatedId}` // `useInteraction` returns interaction state and methods. const [isHovered, isFocused, methods] = useInteraction({}) const { suiInlineClassname } = useStyles(_style, className) @@ -38,6 +41,7 @@ const BuilderButton: FC = ({ return (