Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Guidebanner): convert to .tsx #5321

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

// Import portions of React that are needed.
import React, { useRef, useState } from 'react';
import React, { ReactNode, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { blue90, purple70 } from '@carbon/colors';
Expand All @@ -20,6 +20,60 @@ import { pkg } from '../../settings';
const blockClass = `${pkg.prefix}--guidebanner`;
const componentName = 'Guidebanner';

interface GuidebannerProps {
/**
* Provide the contents of the Guidebanner.
* One or more GuidebannerElement components are required.
*/
children: ReactNode;
/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;
/**
* Tooltip text and aria label for the Close button icon.
*/
closeIconDescription?: string;
/**
* Text label for the Collapse button.
*/
collapseButtonLabel?: string;
/**
* When true, the Guidebanner will initialize in a collapsed state,
* showing the title and the Expand button.
*
* When expanded, it will show the GuidebannerElement child components and the Collapse button.
*/
collapsible?: boolean;
/**
* Text label for the Expand button.
*/
expandButtonLabel?: string;
/**
* Tooltip text and aria label for the Next button icon.
*/
nextIconDescription?: string;
/**
* If defined, a Close button will render in the top-right corner and a
* callback function will be triggered when button is clicked.
*/
onClose?: () => void;
/**
* Tooltip text and aria label for the Back button icon.
*/
previousIconDescription?: string;
/**
* Title text.
*/
title: string;
/**
* If true, insert 1 rem of "space" on the left of the component.
* This will allow the component's content to line up with other
* content on the page under special circumstances.
*/
withLeftGutter?: boolean;
}

const defaults = {
collapsible: false,
withLeftGutter: false,
Expand All @@ -35,7 +89,7 @@ const defaults = {
* The guide banner sits at the top of a page, or page-level tab,
* to introduce foundational concepts related to the page's content.
*/
export let Guidebanner = React.forwardRef(
export let Guidebanner = React.forwardRef<HTMLDivElement, GuidebannerProps>(
(
{
children,
Expand All @@ -54,8 +108,8 @@ export let Guidebanner = React.forwardRef(
},
ref
) => {
const scrollRef = useRef();
const toggleRef = useRef();
const scrollRef = useRef<any>(null);
const toggleRef = useRef<HTMLDivElement>(null);
const [scrollPosition, setScrollPosition] = useState(0);
const [showNavigation, setShowNavigation] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(collapsible ? true : false);
Expand All @@ -67,6 +121,7 @@ export let Guidebanner = React.forwardRef(
return (
<div
{...rest}
aria-expanded={!isCollapsed}
className={cx(
blockClass,
className,
Expand Down Expand Up @@ -106,7 +161,6 @@ export let Guidebanner = React.forwardRef(
className={`${blockClass}__toggle-button`}
onClick={handleClickToggle}
ref={toggleRef}
aria-expanded={!isCollapsed}
>
{isCollapsed ? expandButtonLabel : collapseButtonLabel}
</Button>
Expand Down Expand Up @@ -201,9 +255,10 @@ Guidebanner.propTypes = {
}
React.Children.forEach(prop, (child) => {
if (child.type.displayName !== 'GuidebannerElement') {
// If not GuidebannerElement, then show:
// React component name(child.type?.name) or
// HTML element name(child.type).
// If child element is not `GuidebannerElement`, then show:
// Carbon Products component's `displayName` (child.type.displayName) or
// React component's `name` (child.type.name) or
// HTML element's tag name (child.type).
error = new Error(
`\`Guidebanner\` only accepts children of type \`GuidebannerElement\`, found \`${
child.type?.displayName || child.type?.name || child.type
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

// Import portions of React that are needed.
import React from 'react';
import React, { ReactNode } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { getDevtoolsProps } from '../../global/js/utils/devtools';
Expand All @@ -16,29 +16,47 @@ import { pkg } from '../../settings';
const blockClass = `${pkg.prefix}--guidebanner__element`;
const componentName = 'GuidebannerElement';

interface GuidebannerElementProps {
/**
* An optional button can be rendered below the description.
* This can be a link, button, Coachmark button, etc.
*/
button: ReactNode;
matthewgallo marked this conversation as resolved.
Show resolved Hide resolved
/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;
/**
* The description of the element.
*/
description: ReactNode;
/**
* The title of the element.
*/
title?: string;
}

/**
* The GuidebannerElement is a required child component of the Guidebanner,
* and acts as a container for a CarouselItem.
*/
export let GuidebannerElement = ({
button,
className,
description,
title,
...rest
}) => {
export let GuidebannerElement = React.forwardRef<
HTMLDivElement,
GuidebannerElementProps
>(({ button, className, description, title, ...rest }, ref) => {
return (
<div
{...rest}
className={cx(blockClass, className)}
ref={ref}
{...getDevtoolsProps(componentName)}
>
{title && <h2 className={`${blockClass}-title`}>{title}</h2>}
{description && <p className={`${blockClass}-content`}>{description}</p>}
{button && <div className={`${blockClass}-buttons`}>{button}</div>}
</div>
);
};
});

// Return a placeholder if not released and not enabled by feature flag
GuidebannerElement = pkg.checkComponentEnabled(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

// Import portions of React that are needed.
import React from 'react';
import React, { ReactNode } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { Button } from '@carbon/react';
Expand All @@ -18,23 +18,73 @@ import { pkg } from '../../settings';
const blockClass = `${pkg.prefix}--guidebanner__element-button`;
const componentName = 'GuidebannerElementButton';

interface GuidebannerElementButtonProps {
/**
* Provide the contents of the GuidebannerElementLink.
*/
children: ReactNode;

/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;

/**
* Provide a description for the icon.
*/
iconDescription?: string;

/**
* If type is "primary", then return a tertiary button with the "crossroads" icon,
* else return a ghost button.
*/
type?: string;
}

const defaults = {
iconDescription: 'Crossroads',
};

/**
* One of two buttons styled specifically for the GuidebannerElement.
*/
export let GuidebannerElementButton = ({
children,
className,
type,
...rest
}) => {
if (type === 'primary') {
export let GuidebannerElementButton = React.forwardRef<
Button,
GuidebannerElementButtonProps
>(
(
{
children,
className,
iconDescription = defaults.iconDescription,
type,
...rest
}: GuidebannerElementButtonProps,
ref
) => {
if (type === 'primary') {
return (
<Button
{...rest}
className={cx(blockClass, className)}
iconDescription={iconDescription}
kind="tertiary"
ref={ref}
renderIcon={() => <Crossroads size={16} />}
role="button"
size="md"
{...getDevtoolsProps(componentName)}
>
{children}
</Button>
);
}

return (
<Button
{...rest}
className={cx(blockClass, className)}
iconDescription={'Crossroads'}
kind="tertiary"
renderIcon={() => <Crossroads size={16} />}
kind="ghost"
role="button"
size="md"
{...getDevtoolsProps(componentName)}
Expand All @@ -43,20 +93,7 @@ export let GuidebannerElementButton = ({
</Button>
);
}

return (
<Button
{...rest}
className={cx(blockClass, className)}
kind="ghost"
role="button"
size="md"
{...getDevtoolsProps(componentName)}
>
{children}
</Button>
);
};
);

// Return a placeholder if not released and not enabled by feature flag
GuidebannerElementButton = pkg.checkComponentEnabled(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

// Import portions of React that are needed.
import React from 'react';
import React, { ReactNode } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { Link } from '@carbon/react';
Expand All @@ -17,23 +17,39 @@ import { pkg } from '../../settings';
const blockClass = `${pkg.prefix}--guidebanner__element-link`;
const componentName = 'GuidebannerElementLink';

interface GuidebannerElementLinkProps {
/**
* Provide the contents of the GuidebannerElementLink.
*/
children: ReactNode;

/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;
}

/**
* A link styled specifically for the GuidebannerElement.
*/
export let GuidebannerElementLink = ({ children, className, ...rest }) => {
export let GuidebannerElementLink = React.forwardRef<
Link,
GuidebannerElementLinkProps
>(({ children, className, ...rest }: GuidebannerElementLinkProps, ref) => {
return (
<Link
{...rest}
className={cx(blockClass, className)}
kind="ghost"
ref={ref}
role="link"
size="md"
{...getDevtoolsProps(componentName)}
>
{children}
</Link>
);
};
});

// Return a placeholder if not released and not enabled by feature flag
GuidebannerElementLink = pkg.checkComponentEnabled(
Expand Down
Loading