fix: Tour step.type should work (#45086)

* fix: Tour step.type should work

* chore: code clean

* chore: update memo deps

* chore: code clean
This commit is contained in:
MadCcc 2023-09-26 13:42:43 +08:00 committed by GitHub
parent ef7c500a8e
commit 29be72bc38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 119 deletions

View File

@ -84,7 +84,7 @@ exports[`Tour Primary 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-mask ant-tour-primary" class="ant-tour-mask"
style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;" style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;"
> >
<svg <svg
@ -157,7 +157,7 @@ exports[`Tour Primary 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-primary ant-tour-target-placeholder" class="ant-tour-target-placeholder"
style="left: -6px; top: -6px; width: 12px; height: 12px; position: fixed; pointer-events: none;" style="left: -6px; top: -6px; width: 12px; height: 12px; position: fixed; pointer-events: none;"
/> />
</div> </div>
@ -307,7 +307,7 @@ exports[`Tour controlled current 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-mask ant-tour-primary" class="ant-tour-mask"
style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;" style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;"
> >
<svg <svg
@ -339,7 +339,7 @@ exports[`Tour controlled current 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-primary ant-tour-target-placeholder" class="ant-tour-target-placeholder"
style="left: 50%; top: 50%; width: 1px; height: 1px; position: fixed; pointer-events: none;" style="left: 50%; top: 50%; width: 1px; height: 1px; position: fixed; pointer-events: none;"
/> />
</div> </div>
@ -696,7 +696,7 @@ exports[`Tour step support Primary 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-mask ant-tour-primary" class="ant-tour-mask"
style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;" style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;"
> >
<svg <svg
@ -769,7 +769,7 @@ exports[`Tour step support Primary 1`] = `
</div> </div>
<div> <div>
<div <div
class="ant-tour-primary ant-tour-target-placeholder" class="ant-tour-target-placeholder"
style="left: -6px; top: -6px; width: 12px; height: 12px; position: fixed; pointer-events: none;" style="left: -6px; top: -6px; width: 12px; height: 12px; position: fixed; pointer-events: none;"
/> />
</div> </div>

View File

@ -529,4 +529,38 @@ describe('Tour', () => {
resetIndex(); resetIndex();
}); });
it('first step should be primary', () => {
const App: React.FC = () => {
const coverBtnRef = useRef<HTMLButtonElement>(null);
return (
<>
<button ref={coverBtnRef} type="button">
target
</button>
<Tour
steps={[
{
title: '',
description: '',
target: () => coverBtnRef.current!,
type: 'primary',
className: 'should-be-primary',
},
{
title: '',
target: () => coverBtnRef.current!,
},
]}
/>
</>
);
};
render(<App />);
fireEvent.click(screen.getByRole('button', { name: 'target' }));
expect(document.querySelector('.should-be-primary')).toBeTruthy();
expect(document.querySelector('.should-be-primary')).toHaveClass('ant-tour-primary');
});
}); });

View File

@ -1,60 +0,0 @@
import { act, renderHook } from '../../../tests/utils';
import useMergedType from '../useMergedType';
describe('useMergedType', () => {
it('returns the merged type', () => {
const { result } = renderHook(() =>
useMergedType({
defaultType: 'default',
steps: [{ type: 'primary', title: 'Step 1' }],
current: 0,
}),
);
expect(result.current?.currentMergedType).toBe('primary');
});
it('returns the default type', () => {
const { result } = renderHook(() =>
useMergedType({
defaultType: 'default',
steps: [],
current: 0,
}),
);
expect(result.current?.currentMergedType).toBe('default');
});
it('returns the default type when index is invalid', () => {
const { result } = renderHook(() =>
useMergedType({
defaultType: 'default',
steps: [],
current: 0,
}),
);
expect(result.current?.currentMergedType).toBe('default');
});
it('returns the default type when list is null', () => {
const { result } = renderHook(() =>
useMergedType({
defaultType: 'default',
}),
);
expect(result.current?.currentMergedType).toBe('default');
});
it('returns type of new step after onChange from rc-tour', () => {
const { result } = renderHook(() =>
useMergedType({
defaultType: 'default',
steps: [{ title: 'Step 1' }, { type: 'primary', title: 'Step 1' }],
}),
);
act(() => {
result.current?.updateInnerCurrent?.(1);
});
expect(result.current?.currentMergedType).toBe('primary');
});
});

View File

@ -1,4 +1,4 @@
import React, { useContext } from 'react'; import React, { useContext, useMemo } from 'react';
import RCTour from '@rc-component/tour'; import RCTour from '@rc-component/tour';
import classNames from 'classnames'; import classNames from 'classnames';
@ -10,15 +10,12 @@ import type { TourProps, TourStepProps } from './interface';
import TourPanel from './panelRender'; import TourPanel from './panelRender';
import PurePanel from './PurePanel'; import PurePanel from './PurePanel';
import useStyle from './style'; import useStyle from './style';
import useMergedType from './useMergedType';
const Tour: React.FC<TourProps> & { _InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel } = ( const Tour: React.FC<TourProps> & { _InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel } = (
props, props,
) => { ) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
current,
defaultCurrent,
type, type,
rootClassName, rootClassName,
indicatorsRender, indicatorsRender,
@ -30,12 +27,16 @@ const Tour: React.FC<TourProps> & { _InternalPanelDoNotUseOrYouWillBeFired: type
const [wrapSSR, hashId] = useStyle(prefixCls); const [wrapSSR, hashId] = useStyle(prefixCls);
const [, token] = useToken(); const [, token] = useToken();
const { currentMergedType, updateInnerCurrent } = useMergedType({ const mergedSteps = useMemo(
defaultType: type, () =>
steps, steps?.map((step) => ({
current, ...step,
defaultCurrent, className: classNames(step.className, {
}); [`${prefixCls}-primary`]: (step.type ?? type) === 'primary',
}),
})),
[steps, type],
);
const builtinPlacements = getPlacements({ const builtinPlacements = getPlacements({
arrowPointAtCenter: true, arrowPointAtCenter: true,
@ -47,7 +48,6 @@ const Tour: React.FC<TourProps> & { _InternalPanelDoNotUseOrYouWillBeFired: type
const customClassName = classNames( const customClassName = classNames(
{ {
[`${prefixCls}-primary`]: currentMergedType === 'primary',
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
}, },
hashId, hashId,
@ -63,23 +63,15 @@ const Tour: React.FC<TourProps> & { _InternalPanelDoNotUseOrYouWillBeFired: type
/> />
); );
const onStepChange = (stepCurrent: number) => {
updateInnerCurrent(stepCurrent);
props.onChange?.(stepCurrent);
};
return wrapSSR( return wrapSSR(
<RCTour <RCTour
{...restProps} {...restProps}
rootClassName={customClassName} rootClassName={customClassName}
prefixCls={prefixCls} prefixCls={prefixCls}
current={current}
defaultCurrent={defaultCurrent}
animated animated
renderPanel={mergedRenderPanel} renderPanel={mergedRenderPanel}
builtinPlacements={builtinPlacements} builtinPlacements={builtinPlacements}
onChange={onStepChange} steps={mergedSteps}
steps={steps}
/>, />,
); );
}; };

View File

@ -43,7 +43,6 @@ const TourPanel: React.FC<TourPanelProps> = ({
nextButtonProps, nextButtonProps,
prevButtonProps, prevButtonProps,
type: stepType, type: stepType,
className,
closeIcon: stepCloseIcon, closeIcon: stepCloseIcon,
} = stepProps; } = stepProps;
@ -120,7 +119,7 @@ const TourPanel: React.FC<TourPanelProps> = ({
const [contextLocale] = useLocale('Tour', defaultLocale.Tour); const [contextLocale] = useLocale('Tour', defaultLocale.Tour);
return ( return (
<div className={classNames(className, `${prefixCls}-content`)}> <div className={`${prefixCls}-content`}>
<div className={`${prefixCls}-inner`}> <div className={`${prefixCls}-inner`}>
{closable && mergedDisplayCloseIcon} {closable && mergedDisplayCloseIcon}
{coverNode} {coverNode}

View File

@ -1,31 +0,0 @@
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import { useLayoutEffect } from 'react';
import type { TourProps } from './interface';
interface Props {
defaultType?: string;
steps?: TourProps['steps'];
current?: number;
defaultCurrent?: number;
}
/**
* returns the merged type of a step or the default type.
*/
const useMergedType = ({ defaultType, steps = [], current, defaultCurrent }: Props) => {
const [innerCurrent, updateInnerCurrent] = useMergedState<number | undefined>(defaultCurrent, {
value: current,
});
useLayoutEffect(() => {
if (current === undefined) return;
updateInnerCurrent(current);
}, [current]);
const innerType = typeof innerCurrent === 'number' ? steps[innerCurrent]?.type : defaultType;
const currentMergedType = innerType ?? defaultType;
return { currentMergedType, updateInnerCurrent };
};
export default useMergedType;