Skip to content

Commit

Permalink
feat: Anchor component changed to data-driven (ant-design#39034)
Browse files Browse the repository at this point in the history
* feat: Anchor component changed to data-driven

* test: add test cases for data driven items

* fix: type

* chore: mark deprecated for anchor children prop

* docs: add items description

* test: update snapshot

* docs: demos changed to data-driven

* docs: Keep the old jsx syntax demo for debugging
  • Loading branch information
foryuki authored and heiyu4585 committed Dec 16, 2022
1 parent fcd9080 commit d7df147
Show file tree
Hide file tree
Showing 12 changed files with 489 additions and 43 deletions.
29 changes: 28 additions & 1 deletion components/anchor/Anchor.tsx
Expand Up @@ -6,10 +6,18 @@ import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import getScroll from '../_util/getScroll';
import scrollTo from '../_util/scrollTo';
import warning from '../_util/warning';
import AnchorContext from './context';
import type { AnchorLinkBaseProps } from './AnchorLink';
import AnchorLink from './AnchorLink';

import useStyle from './style';

export interface AnchorLinkItemProps extends AnchorLinkBaseProps {
key: React.Key;
children?: AnchorLinkItemProps[];
}

export type AnchorContainer = HTMLElement | Window;

function getDefaultContainer() {
Expand Down Expand Up @@ -45,6 +53,9 @@ export interface AnchorProps {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
/**
* @deprecated Please use `items` instead.
*/
children?: React.ReactNode;
offsetTop?: number;
bounds?: number;
Expand All @@ -61,6 +72,7 @@ export interface AnchorProps {
targetOffset?: number;
/** Listening event when scrolling change active link */
onChange?: (currentActiveLink: string) => void;
items?: AnchorLinkItemProps[];
}

interface InternalAnchorProps extends AnchorProps {
Expand Down Expand Up @@ -100,6 +112,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
affix = true,
showInkInFixed = false,
children,
items,
bounds,
targetOffset,
onClick,
Expand All @@ -108,6 +121,11 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
getCurrentAnchor,
} = props;

// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
warning(!children, 'Anchor', '`Anchor children` is deprecated. Please use `items` instead.');
}

const [links, setLinks] = React.useState<string[]>([]);
const [activeLink, setActiveLink] = React.useState<string | null>(null);
const activeLinkRef = React.useRef<string | null>(activeLink);
Expand Down Expand Up @@ -257,13 +275,22 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
...style,
};

const createNestedLink = (options?: AnchorLinkItemProps[]) =>
Array.isArray(options)
? options.map((item) => (
<AnchorLink {...item} key={item.key}>
{createNestedLink(item.children)}
</AnchorLink>
))
: null;

const anchorContent = (
<div ref={wrapperRef} className={wrapperClass} style={wrapperStyle}>
<div className={anchorClass}>
<div className={`${prefixCls}-ink`}>
<span className={inkClass} ref={spanLinkNode} />
</div>
{children}
{'items' in props ? createNestedLink(items) : children}
</div>
</div>
);
Expand Down
7 changes: 5 additions & 2 deletions components/anchor/AnchorLink.tsx
Expand Up @@ -5,15 +5,18 @@ import { ConfigConsumer } from '../config-provider';
import type { AntAnchor } from './Anchor';
import AnchorContext from './context';

export interface AnchorLinkProps {
export interface AnchorLinkBaseProps {
prefixCls?: string;
href: string;
target?: string;
title: React.ReactNode;
children?: React.ReactNode;
className?: string;
}

export interface AnchorLinkProps extends AnchorLinkBaseProps {
children?: React.ReactNode;
}

const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
const { href = '#', title, prefixCls: customizePrefixCls, children, className, target } = props;

Expand Down
60 changes: 60 additions & 0 deletions components/anchor/__tests__/Anchor.test.tsx
Expand Up @@ -432,4 +432,64 @@ describe('Anchor Render', () => {
}).not.toThrow();
});
});

it('renders items correctly', () => {
const { container, asFragment } = render(
<Anchor
items={[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Item Basic Demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
children: [
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
],
},
]}
/>,
);
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(5);
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!)
.slice(1)
.map((n) => (n as HTMLElement).querySelector('.ant-anchor-link-title'));
expect((linkTitles[0] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#api');
expect(asFragment().firstChild).toMatchSnapshot();
expect(
(
container.querySelector(
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
) as HTMLAnchorElement
)?.href,
).toContain('#anchor-props');
expect(
(
container.querySelector(
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
) as HTMLAnchorElement
)?.href,
).toContain('#link-props');
});
});
81 changes: 81 additions & 0 deletions components/anchor/__tests__/__snapshots__/Anchor.test.tsx.snap
@@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Anchor Render renders items correctly 1`] = `
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height: 100vh;"
>
<div
class="ant-anchor"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Item Basic Demo"
>
Item Basic Demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#api"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#anchor-props"
title="Anchor Props"
>
Anchor Props
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#link-props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
80 changes: 80 additions & 0 deletions components/anchor/__tests__/__snapshots__/demo-extend.test.ts.snap
Expand Up @@ -156,6 +156,86 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context
</div>
`;

exports[`renders ./components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Basic demo"
>
Basic demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#api"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#anchor-props"
title="Anchor Props"
>
Anchor Props
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#link-props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
</div>
</div>
`;

exports[`renders ./components/anchor/demo/onChange.tsx extend context correctly 1`] = `
<div
class="ant-anchor-wrapper"
Expand Down

0 comments on commit d7df147

Please sign in to comment.