Skip to content

Commit

Permalink
refactor WizardToggle a bit more and address feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuzz0 committed Sep 19, 2022
1 parent 57e59ef commit b36baeb
Show file tree
Hide file tree
Showing 13 changed files with 668 additions and 637 deletions.
63 changes: 31 additions & 32 deletions packages/react-core/src/next/components/Wizard/Wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
CustomWizardNavFunction
} from './types';
import { buildSteps, normalizeNavStep } from './utils';
import { useWizardContext, WizardContextProvider } from './WizardContext';
import { WizardContextProvider } from './WizardContext';
import { WizardStepProps } from './WizardStep';
import { WizardToggle } from './WizardToggle';
import { WizardToggleInternal } from './WizardToggle';

/**
* Wrapper for all steps and hosts state, including navigation helpers, within context.
Expand All @@ -27,7 +27,7 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
header?: React.ReactNode;
/** Wizard footer */
footer?: DefaultWizardFooterProps | React.ReactElement;
/** Default wizard nav props or a custom WizardNav (with callback) */
/** Wizard nav */
nav?: DefaultWizardNavProps | CustomWizardNavFunction;
/** The initial index the wizard is to start on (1 or higher). Defaults to 1. */
startIndex?: number;
Expand All @@ -51,8 +51,23 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
onClose?: () => void;
}

export const Wizard = (props: WizardProps) => {
const { startIndex = 1, children, footer, onNavByIndex, onNext, onBack, onSave, onClose, ...internalProps } = props;
export const Wizard = ({
startIndex = 1,
children,
footer,
height,
width,
className,
header,
nav,
unmountInactiveSteps,
onNavByIndex,
onNext,
onBack,
onSave,
onClose,
...wrapperProps
}: WizardProps) => {
const [currentStepIndex, setCurrentStepIndex] = React.useState(startIndex);
const steps = buildSteps(children);

Expand Down Expand Up @@ -138,35 +153,19 @@ export const Wizard = (props: WizardProps) => {
goToStepByName={goToStepByName}
goToStepByIndex={goToStepByIndex}
>
<WizardInternal {...internalProps}>{children}</WizardInternal>
<div
className={css(styles.wizard, className)}
style={{
...(height ? { height } : {}),
...(width ? { width } : {})
}}
{...wrapperProps}
>
{header}
<WizardToggleInternal nav={nav} unmountInactiveSteps={unmountInactiveSteps} />
</div>
</WizardContextProvider>
);
};

// eslint-disable-next-line patternfly-react/no-anonymous-functions
const WizardInternal = ({ height, width, className, header, nav, unmountInactiveSteps, ...divProps }: WizardProps) => {
const { activeStep, steps, footer, goToStepByIndex } = useWizardContext();

return (
<div
className={css(styles.wizard, className)}
style={{
...(height ? { height } : {}),
...(width ? { width } : {})
}}
{...divProps}
>
{header}
<WizardToggle
steps={steps}
activeStep={activeStep}
footer={footer}
nav={nav}
goToStepByIndex={goToStepByIndex}
unmountInactiveSteps={unmountInactiveSteps}
/>
</div>
);
};

Wizard.displayName = 'Wizard';
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const WizardContext = React.createContext({} as WizardContextProps);
interface WizardContextRenderProps {
steps: WizardControlStep[];
activeStep: WizardControlStep;
footer: DefaultWizardFooterProps | React.ReactElement;
footer: React.ReactElement;
onNext(): void;
onBack(): void;
onClose(): void;
Expand Down Expand Up @@ -118,7 +118,9 @@ export const WizardContextProvider: React.FunctionComponent<WizardContextProvide
setFooter
}}
>
{typeof children === 'function' ? children({ activeStep, steps, footer, onNext, onBack, onClose }) : children}
{typeof children === 'function'
? children({ activeStep, steps, footer: wizardFooter, onNext, onBack, onClose })
: children}
</WizardContext.Provider>
);
};
Expand Down
6 changes: 3 additions & 3 deletions packages/react-core/src/next/components/Wizard/WizardNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface WizardNavProps {
/** Sets the aria-labelledby attribute on the nav element */
'aria-labelledby'?: string;
/** Whether the nav is expanded */
isOpen?: boolean;
isExpanded?: boolean;
/** True to return the inner list without the wrapping nav element */
returnList?: boolean;
}
Expand All @@ -19,7 +19,7 @@ export const WizardNav: React.FunctionComponent<WizardNavProps> = ({
children,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
isOpen = false,
isExpanded = false,
returnList = false
}: WizardNavProps) => {
const innerList = <ol className={css(styles.wizardNavList)}>{children}</ol>;
Expand All @@ -30,7 +30,7 @@ export const WizardNav: React.FunctionComponent<WizardNavProps> = ({

return (
<nav
className={css(styles.wizardNav, isOpen && styles.modifiers.expanded)}
className={css(styles.wizardNav, isExpanded && styles.modifiers.expanded)}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
>
Expand Down
121 changes: 73 additions & 48 deletions packages/react-core/src/next/components/Wizard/WizardToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-i
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';

import { KeyTypes } from '../../../helpers/constants';
import { WizardNav, WizardNavItem } from '../Wizard';
import { WizardNav, WizardNavItem, WizardNavProps } from '../Wizard';
import {
WizardControlStep,
CustomWizardNavFunction,
DefaultWizardNavProps,
isWizardBasicStep,
isWizardParentStep,
isWizardSubStep,
isCustomWizardNav
isCustomWizardNav,
DefaultWizardNavProps,
CustomWizardNavFunction
} from './types';
import { useWizardContext } from './WizardContext';

/**
* Used to toggle between step content, including the body and footer. This is also where the nav and its expandability is controlled.
Expand All @@ -26,37 +27,39 @@ export interface WizardToggleProps {
steps: WizardControlStep[];
/** The currently active WizardStep */
activeStep: WizardControlStep;
/** The WizardFooter */
/** Wizard footer */
footer: React.ReactElement;
/** Navigate using the step index */
goToStepByIndex: (index: number) => void;
/** Custom WizardNav or callback used to create a default WizardNav */
nav?: DefaultWizardNavProps | CustomWizardNavFunction;
/** Wizard nav */
nav: React.ReactElement<WizardNavProps>;
/** The expandable dropdown button's aria-label */
'aria-label'?: string;
/** Flag to unmount inactive steps instead of hiding. Defaults to true */
unmountInactiveSteps?: boolean;
/** Flag to determine whether the dropdown nav is expanded */
isNavExpanded?: boolean;
/** Callback to expand or collapse the dropdown nav */
toggleNavExpanded?: () => void;
}

export const WizardToggle = ({
steps,
activeStep,
footer,
nav,
goToStepByIndex,
isNavExpanded,
toggleNavExpanded,
unmountInactiveSteps = true,
'aria-label': ariaLabel = 'Wizard toggle'
}: WizardToggleProps) => {
const [isNavOpen, setIsNavOpen] = React.useState(false);
const isActiveSubStep = isWizardSubStep(activeStep);

const handleKeyClicks = React.useCallback(
(event: KeyboardEvent): void => {
if (isNavOpen && event.key === KeyTypes.Escape) {
setIsNavOpen(!isNavOpen);
if (isNavExpanded && event.key === KeyTypes.Escape) {
toggleNavExpanded();
}
},
[isNavOpen]
[isNavExpanded, toggleNavExpanded]
);

// Open/close collapsable nav on keydown event
Expand Down Expand Up @@ -84,19 +87,60 @@ export const WizardToggle = ({
);
});

return (
<>
<button
onClick={toggleNavExpanded}
className={css(styles.wizardToggle, isNavExpanded && 'pf-m-expanded')}
aria-label={ariaLabel}
aria-expanded={isNavExpanded}
>
<span className={css(styles.wizardToggleList)}>
<span className={css(styles.wizardToggleListItem)}>
{activeStep?.name}
{isActiveSubStep && <AngleRightIcon className={css(styles.wizardToggleSeparator)} aria-hidden="true" />}
</span>
{isActiveSubStep && <span className={css(styles.wizardToggleListItem)}>{activeStep?.name}</span>}
</span>

<span className={css(styles.wizardToggleIcon)}>
<CaretDownIcon aria-hidden="true" />
</span>
</button>
<div className={css(styles.wizardOuterWrap)}>
<div className={css(styles.wizardInnerWrap)}>
{nav}
{bodyContent}
</div>

{footer}
</div>
</>
);
};

interface WizardToggleInternalProps extends Pick<WizardToggleProps, 'unmountInactiveSteps'> {
/** Custom WizardNav or callback used to create a default WizardNav */
nav?: DefaultWizardNavProps | CustomWizardNavFunction;
}

export const WizardToggleInternal = ({ nav, unmountInactiveSteps }: WizardToggleInternalProps) => {
const [isNavExpanded, setIsNavExpanded] = React.useState(false);
const { activeStep, steps, footer, goToStepByIndex } = useWizardContext();

const wizardNav = React.useMemo(() => {
if (isCustomWizardNav(nav)) {
return nav(isNavOpen, steps, activeStep, goToStepByIndex);
return nav(isNavExpanded, steps, activeStep, goToStepByIndex);
}

const props = {
isOpen: isNavOpen,
const navProps = {
isExpanded: isNavExpanded,
'aria-label': nav?.ariaLabel || 'Wizard nav',
...(nav?.ariaLabelledBy && { 'aria-labelledby': nav?.ariaLabelledBy })
};

return (
<WizardNav {...props}>
<WizardNav {...navProps}>
{steps.map((step, index) => {
const stepIndex = index + 1;
const stepNavItem = step.navItem && <React.Fragment key={step.id}>{step.navItem}</React.Fragment>;
Expand Down Expand Up @@ -148,7 +192,7 @@ export const WizardToggle = ({
step={firstSubStepIndex}
onNavItemClick={goToStepByIndex}
>
<WizardNav {...props} returnList>
<WizardNav {...navProps} returnList>
{subNavItems}
</WizardNav>
</WizardNavItem>
Expand All @@ -174,37 +218,18 @@ export const WizardToggle = ({
})}
</WizardNav>
);
}, [activeStep?.id, goToStepByIndex, isNavOpen, nav, steps]);
}, [activeStep, goToStepByIndex, isNavExpanded, nav, steps]);

return (
<>
<button
onClick={() => setIsNavOpen(prevIsOpen => !prevIsOpen)}
className={css(styles.wizardToggle, isNavOpen && 'pf-m-expanded')}
aria-label={ariaLabel}
aria-expanded={isNavOpen}
>
<span className={css(styles.wizardToggleList)}>
<span className={css(styles.wizardToggleListItem)}>
{activeStep?.name}
{isActiveSubStep && <AngleRightIcon className={css(styles.wizardToggleSeparator)} aria-hidden="true" />}
</span>
{isActiveSubStep && <span className={css(styles.wizardToggleListItem)}>{activeStep?.name}</span>}
</span>

<span className={css(styles.wizardToggleIcon)}>
<CaretDownIcon aria-hidden="true" />
</span>
</button>
<div className={css(styles.wizardOuterWrap)}>
<div className={css(styles.wizardInnerWrap)}>
{wizardNav}
{bodyContent}
</div>

{footer}
</div>
</>
<WizardToggle
nav={wizardNav}
footer={footer}
steps={steps}
activeStep={activeStep}
isNavExpanded={isNavExpanded}
toggleNavExpanded={() => setIsNavExpanded(prevIsExpanded => !prevIsExpanded)}
unmountInactiveSteps={unmountInactiveSteps}
/>
);
};

Expand Down
Loading

0 comments on commit b36baeb

Please sign in to comment.