@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
95 lines • 8.9 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import clsx from 'clsx';
import { warnOnce } from '@awsui/component-toolkit/internal';
import { getAnalyticsMetadataAttribute } from '@awsui/component-toolkit/internal/analytics-metadata';
import InternalHeader, { Description as HeaderDescription } from '../header/internal';
import InternalIcon from '../icon/internal';
import { isDevelopment } from '../internal/is-development';
import { variantRequiresActionsDivider, variantSupportsActions, variantSupportsDescription, variantSupportsInfoLink, } from './utils';
import analyticsSelectors from './analytics-metadata/styles.css.js';
import styles from './styles.css.js';
const componentName = 'ExpandableSection';
const getExpandActionAnalyticsMetadataAttribute = (expanded) => {
const metadata = {
action: !expanded ? 'expand' : 'collapse',
detail: {
label: { rootSelector: `.${analyticsSelectors.root}` },
},
};
return getAnalyticsMetadataAttribute(metadata);
};
const ExpandableDeprecatedHeader = ({ id, className, onClick, ariaLabel, ariaControls, expanded, children, icon, onKeyUp, onKeyDown, variant, }) => {
return (React.createElement("div", { id: id, role: "button", className: clsx(className, styles['expand-button'], styles['click-target'], styles['header-deprecated'], analyticsSelectors['header-label']), tabIndex: 0, onKeyUp: onKeyUp, onKeyDown: onKeyDown, onClick: onClick, "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-expanded": expanded, ...getExpandActionAnalyticsMetadataAttribute(expanded) },
React.createElement("div", { className: clsx(styles['icon-container'], styles[`icon-container-${variant}`]) }, icon),
children));
};
const ExpandableNavigationHeader = ({ id, className, onClick, ariaLabelledBy, ariaLabel, ariaControls, expanded, children, icon, }) => {
return (React.createElement("div", { id: id, className: clsx(className, styles['click-target'], analyticsSelectors['header-label']) },
React.createElement("button", { className: clsx(styles['icon-container'], styles['expand-button']), "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabel, "aria-controls": ariaControls, "aria-expanded": expanded, type: "button", onClick: onClick, ...getExpandActionAnalyticsMetadataAttribute(expanded) }, icon),
children));
};
const ExpandableHeaderTextWrapper = ({ id, descriptionId, className, onClick, ariaLabel, ariaControls, expanded, children, icon, headerDescription, headerCounter, headerInfo, headerActions, variant, headingTagOverride, onKeyUp, onKeyDown, }) => {
const isContainer = variant === 'container';
const HeadingTag = headingTagOverride || 'div';
const supportsInteractiveElements = variantSupportsActions(variant);
const restrictClickableArea = supportsInteractiveElements && (headerInfo || headerActions);
const actions = supportsInteractiveElements && headerActions;
const description = variantSupportsDescription(variant) && headerDescription && (React.createElement("span", { id: descriptionId, className: styles[`description-${variant}`] }, headerDescription));
const listeners = { onClick, onKeyDown, onKeyUp };
// If interactive elements are present, constrain the clickable area to only the icon and the header text
// to prevent nesting interactive elements.
const headerButtonListeners = restrictClickableArea ? listeners : undefined;
// For the default and footer variants with description,
// include also the immediate wrapper around it to include the entire row for backwards compatibility,
// but exclude the description.
const headingTagListeners = !headerButtonListeners && !isContainer && description ? listeners : undefined;
// For all other cases, make the entire header clickable for backwards compatibility.
const wrapperListeners = !headerButtonListeners && !headingTagListeners ? listeners : undefined;
const headerButton = (React.createElement("span", { className: clsx(styles['expand-button'], isContainer ? styles['header-container-button'] : styles['header-button'], headerButtonListeners && styles['click-target']), role: "button", tabIndex: 0, "aria-label": ariaLabel, "aria-labelledby": !ariaLabel && description ? id : undefined, "aria-describedby": description ? descriptionId : undefined, "aria-controls": ariaControls, "aria-expanded": expanded, ...headerButtonListeners, ...(headerButtonListeners ? getExpandActionAnalyticsMetadataAttribute(expanded) : {}) },
React.createElement("span", { className: clsx(styles['icon-container'], styles[`icon-container-${variant}`]) }, icon),
React.createElement("span", { id: id, className: clsx(styles['header-text'], analyticsSelectors['header-label']) }, children)));
return (React.createElement("div", { className: clsx(className, wrapperListeners && styles['click-target']), ...wrapperListeners, ...(wrapperListeners ? getExpandActionAnalyticsMetadataAttribute(expanded) : {}) }, isContainer ? (React.createElement(InternalHeader, { variant: "h2", description: description, counter: headerCounter, info: headerInfo, actions: actions, headingTagOverride: headingTagOverride }, headerButton)) : (React.createElement(React.Fragment, null,
React.createElement("div", { className: clsx(actions && styles['header-actions-wrapper']) },
React.createElement(HeadingTag, { className: clsx(styles['header-wrapper'], headingTagListeners && styles['click-target']), ...headingTagListeners, ...(headingTagListeners ? getExpandActionAnalyticsMetadataAttribute(expanded) : {}) }, headerButton),
actions),
description && React.createElement(HeaderDescription, { variantOverride: "h3" }, description)))));
};
export const ExpandableSectionHeader = ({ id, descriptionId, className, variant, header, headerText, headerDescription, headerCounter, headerInfo, headerActions, headingTagOverride, expanded, ariaControls, ariaLabel, ariaLabelledBy, onKeyUp, onKeyDown, onClick, }) => {
const alwaysShowDivider = variantRequiresActionsDivider(variant) && headerActions;
const icon = (React.createElement(InternalIcon, { size: variant === 'container' ? 'medium' : 'normal', className: clsx(styles.icon, expanded && styles.expanded), name: "caret-down-filled" }));
const defaultHeaderProps = {
id: id,
icon: icon,
expanded: expanded,
ariaControls: ariaControls,
ariaLabel: ariaLabel,
onClick: onClick,
variant,
};
if ((headerCounter || headerInfo) && !variantSupportsInfoLink(variant) && isDevelopment) {
warnOnce(componentName, 'The `headerCounter` and `headerInfo` props are only supported for the "container" variant.');
}
if (headerActions && !variantSupportsActions(variant) && isDevelopment) {
warnOnce(componentName, `The \`headerActions\` prop is only supported for the "container" and "default" variants.`);
}
if (headerDescription && !variantSupportsDescription(variant) && isDevelopment) {
warnOnce(componentName, `The \`headerDescription\` prop is not supported for the ${variant} variant.`);
}
const wrapperClassName = clsx(styles.wrapper, styles[`wrapper-${variant}`], (expanded || alwaysShowDivider) && styles['wrapper-expanded']);
if (variant === 'navigation') {
return (React.createElement(ExpandableNavigationHeader, { className: clsx(className, wrapperClassName), ariaLabelledBy: ariaLabelledBy, ...defaultHeaderProps }, headerText !== null && headerText !== void 0 ? headerText : header));
}
if (headerText || variant === 'inline') {
if (!headerText && header && variant === 'inline') {
warnOnce(componentName, 'Only `headerText` instead of `header` is supported for `inline` variant.');
}
return (React.createElement(ExpandableHeaderTextWrapper, { className: clsx(className, wrapperClassName, expanded && styles.expanded, !expanded && (!headerActions || headerDescription) && styles['wrapper-not-expanded-without-actions']), descriptionId: descriptionId, headerDescription: headerDescription, headerCounter: headerCounter, headerInfo: headerInfo, headerActions: headerActions, headingTagOverride: headingTagOverride, onKeyUp: onKeyUp, onKeyDown: onKeyDown, ...defaultHeaderProps }, headerText));
}
if (variant === 'container' && header && isDevelopment) {
warnOnce(componentName, 'Use `headerText` instead of `header` to provide the button within the heading for a11y.');
}
return (React.createElement(ExpandableDeprecatedHeader, { className: clsx(className, wrapperClassName, styles.focusable, expanded && styles.expanded), onKeyUp: onKeyUp, onKeyDown: onKeyDown, ...defaultHeaderProps }, header));
};
//# sourceMappingURL=expandable-section-header.js.map