Skip to content

Commit

Permalink
refactor(view-switcher): enhance ViewSwitcherButton types and improve…
Browse files Browse the repository at this point in the history
… component structure and documentation
  • Loading branch information
misama-ct committed Jan 21, 2025
1 parent ca01f2b commit 9332aa7
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 18 deletions.
25 changes: 22 additions & 3 deletions packages/components/view-switcher/src/view-switcher-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,48 @@ import {
type MouseEvent,
type KeyboardEvent,
type ReactElement,
FC,
} from 'react';
import { css } from '@emotion/react';
import { designTokens } from '@commercetools-uikit/design-system';
import AccessibleButton from '@commercetools-uikit/accessible-button';
import { warning } from '@commercetools-uikit/utils';
import { getButtonStyles } from './view-switcher.styles';

/**
* Props for the ViewSwitcherButton component
*/
export type TViewSwitcherButtonProps = {
/** The text content to be displayed inside the button */
children?: string;
/** An icon element to be displayed inside the button */
icon?: ReactElement;
/** Indicates if the button is in active state */
isActive?: boolean;
/** Indicates if the button is disabled */
isDisabled?: boolean;
/** Indicates if the button should be rendered in condensed mode */
isCondensed?: boolean;
/** Indicates if this button is the first in a group of buttons */
isFirstButton?: boolean;
/** Indicates if this button is the last in a group of buttons */
isLastButton?: boolean;
/** Accessible label for the button (required when only icon is provided) */
label?: string;
/** Value associated with the button */
value: string;
/** Handler called when the button is clicked */
onClick?: (
event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement>
event:
| MouseEvent<HTMLButtonElement>
| KeyboardEvent<HTMLButtonElement>
| string
) => void;
};

const ViewSwitcherButton = (props: TViewSwitcherButtonProps) => {
export type TViewSwitcherButtonElement = FC<TViewSwitcherButtonProps>;

const ViewSwitcherButton: TViewSwitcherButtonElement = (props) => {
warning(
Boolean(props.children || props.icon),
'uikit/ViewSwitcherButton: You need to provide at least the `children` to render inside the button or an `icon`.'
Expand Down Expand Up @@ -68,5 +87,5 @@ const ViewSwitcherButton = (props: TViewSwitcherButtonProps) => {
);
};

ViewSwitcherButton.displayName = 'ViewSwitcherButton';
ViewSwitcherButton.displayName = 'ViewSwitcher.Button';
export default ViewSwitcherButton;
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const meta: Meta<typeof ViewSwitcher.Group> = {
title: 'components/ViewSwitcher',
component: ViewSwitcher.Group,
subcomponents: {
// @ts-ignore
Button: ViewSwitcher.Button,
// @ts-expect-error
'ViewSwitcher.Button': ViewSwitcher.Button,
},
argTypes: {
children: { control: false },
Expand Down
25 changes: 12 additions & 13 deletions packages/components/view-switcher/src/view-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ import {
isValidElement,
cloneElement,
useState,
type ReactNode,
type ReactElement,
} from 'react';
import isNil from 'lodash/isNil';
import { css } from '@emotion/react';
import ViewSwitcherButton from './view-switcher-button';
import ViewSwitcherButton, {
TViewSwitcherButtonProps,
} from './view-switcher-button';
import { warning } from '@commercetools-uikit/utils';

type TReactChild = {
type?: { displayName: string };
} & ReactElement;
type TViewSwitcherButtonElement = ReactElement<TViewSwitcherButtonProps>;

export type TViewSwitcherProps = {
/**
Expand All @@ -23,7 +22,7 @@ export type TViewSwitcherProps = {
/**
* Pass one or more `ViewSwitcher.Button` components.
*/
children: ReactNode;
children: TViewSwitcherButtonElement | TViewSwitcherButtonElement[];
/**
* Will be triggered whenever a `ViewSwitcher.Button` is selected. Called with the ViewSwitcherButton value.
* This function is only required when the component is controlled.
Expand Down Expand Up @@ -65,20 +64,20 @@ const ViewSwitcher = (props: TViewSwitcherProps) => {
);

warning(
(props.children as TReactChild[]).length > 0,
(props.children as TViewSwitcherButtonElement[]).length > 0,
'ViewSwitcher.Group must contain at least one ViewSwitcher.Button'
);

const viewSwitcherElements = Children.map(props.children, (child, index) => {
if (
child &&
isValidElement(child) &&
(child as TReactChild).type.displayName === ViewSwitcherButton.displayName
isValidElement<TViewSwitcherButtonProps>(child) &&
child.type === ViewSwitcherButton
) {
const isButtonActive =
(isControlledComponent ? props.selectedValue : selectedButton) ===
child.props.value;
const clonedChild = cloneElement(child as TReactChild, {
return cloneElement(child, {
onClick: () => {
if (!isControlledComponent) {
setSelectedButton(child.props.value);
Expand All @@ -92,10 +91,8 @@ const ViewSwitcher = (props: TViewSwitcherProps) => {
isCondensed: props.isCondensed,
isActive: isButtonActive,
isFirstButton: index === 0,
isLastButton:
index === ((props.children as TReactChild[]).length ?? 1) - 1,
isLastButton: index === Children.count(props.children) - 1,
});
return clonedChild;
}
return child;
});
Expand All @@ -111,4 +108,6 @@ const ViewSwitcher = (props: TViewSwitcherProps) => {
);
};

ViewSwitcher.displayName = 'ViewSwitcher.Group';

export default ViewSwitcher;

0 comments on commit 9332aa7

Please sign in to comment.