Skip to content

Commit 7cb808e

Browse files
authored
Merge pull request #1546 from easyops-cn/steve/toggle-link
feat(): new brick: eo-toggle-link
2 parents ba8a85d + e6ea80f commit 7cb808e

File tree

6 files changed

+157
-1
lines changed

6 files changed

+157
-1
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
展开/折叠链接。
2+
3+
## Examples
4+
5+
### Basic
6+
7+
```yaml preview
8+
- brick: eo-toggle-link
9+
context:
10+
- name: open
11+
value: false
12+
properties:
13+
textContent: Toggle me
14+
events:
15+
toggle:
16+
action: context.replace
17+
args:
18+
- open
19+
- <% EVENT.detail %>
20+
- brick: p
21+
properties:
22+
textContent: I see you!
23+
hidden: <%= !CTX.open %>
24+
```

bricks/basic/src/bootstrap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,4 @@ import "./custom-processors/smartDisplayForEvaluableString.js";
5252
import "./viewport/index.js";
5353
import "./data-providers/require-modal-stack.js";
5454
import "./list/index.js";
55+
import "./toggle-link/index.js";
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { describe, test, expect, jest } from "@jest/globals";
2+
import { act } from "react-dom/test-utils";
3+
import { fireEvent } from "@testing-library/dom";
4+
import "./";
5+
import type { ToggleLink } from "./index.js";
6+
7+
jest.mock("@next-core/theme", () => ({}));
8+
9+
describe("eo-toggle-link", () => {
10+
test("basic usage", async () => {
11+
const element = document.createElement("eo-toggle-link") as ToggleLink;
12+
13+
expect(element.shadowRoot).toBeFalsy();
14+
const onToggle = jest.fn();
15+
element.addEventListener("toggle", (e) => {
16+
onToggle((e as CustomEvent).detail);
17+
});
18+
19+
act(() => {
20+
document.body.appendChild(element);
21+
});
22+
expect(element.shadowRoot?.childNodes.length).toBeGreaterThan(1);
23+
24+
expect(element.open).toBeFalsy();
25+
expect(onToggle).toHaveBeenCalledTimes(0);
26+
27+
fireEvent.click(element.shadowRoot!.querySelector("eo-link")!);
28+
expect(element.open).toBe(true);
29+
expect(onToggle).toHaveBeenCalledTimes(1);
30+
expect(onToggle).toHaveBeenLastCalledWith(true);
31+
32+
fireEvent.click(element.shadowRoot!.querySelector("eo-link")!);
33+
expect(element.open).toBe(false);
34+
expect(onToggle).toHaveBeenCalledTimes(2);
35+
expect(onToggle).toHaveBeenLastCalledWith(false);
36+
37+
act(() => {
38+
document.body.removeChild(element);
39+
});
40+
expect(element.shadowRoot?.childNodes.length).toBe(0);
41+
});
42+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React from "react";
2+
import { createDecorators, type EventEmitter } from "@next-core/element";
3+
import { ReactNextElement, wrapBrick } from "@next-core/react-element";
4+
import "@next-core/theme";
5+
import type {
6+
GeneralIcon,
7+
GeneralIconProps,
8+
} from "@next-bricks/icons/general-icon";
9+
import type { Link, LinkProps } from "../link/index.js";
10+
import styleText from "./styles.shadow.css";
11+
12+
const WrappedLink = wrapBrick<Link, LinkProps>("eo-link");
13+
const WrappedIcon = wrapBrick<GeneralIcon, GeneralIconProps>("eo-icon");
14+
15+
const { defineElement, property, event } = createDecorators();
16+
17+
export interface ToggleLinkProps {
18+
open?: boolean;
19+
themeVariant?: "default" | "elevo";
20+
}
21+
22+
/**
23+
* 展开/折叠链接。
24+
*
25+
* @slot - 内容
26+
* @part link - 链接
27+
* @part icon - 图标
28+
*/
29+
export
30+
@defineElement("eo-toggle-link", {
31+
styleTexts: [styleText],
32+
})
33+
class ToggleLink extends ReactNextElement implements ToggleLinkProps {
34+
@property({ type: Boolean, render: false })
35+
accessor open: boolean | undefined;
36+
37+
/** 主题变体 */
38+
@property()
39+
accessor themeVariant: "default" | "elevo" | undefined;
40+
41+
@event({ type: "toggle" })
42+
accessor #toggle!: EventEmitter<boolean>;
43+
44+
#handleToggle = () => {
45+
this.#toggle.emit((this.open = !this.open));
46+
};
47+
48+
render() {
49+
return (
50+
<ToggleLinkComponent
51+
open={this.open}
52+
themeVariant={this.themeVariant}
53+
onToggle={this.#handleToggle}
54+
/>
55+
);
56+
}
57+
}
58+
59+
interface ToggleLinkComponentProps extends ToggleLinkProps {
60+
onToggle: () => void;
61+
}
62+
63+
function ToggleLinkComponent({
64+
themeVariant,
65+
onToggle,
66+
}: ToggleLinkComponentProps) {
67+
return (
68+
<WrappedLink themeVariant={themeVariant} part="link" onClick={onToggle}>
69+
<slot />
70+
<WrappedIcon part="icon" className="chevron" lib="fa" icon="angle-down" />
71+
</WrappedLink>
72+
);
73+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
:host {
2+
display: block;
3+
}
4+
5+
:host([hidden]) {
6+
display: none;
7+
}
8+
9+
.chevron {
10+
transition: transform 0.2s ease-in-out;
11+
}
12+
13+
:host([open]) .chevron {
14+
transform: rotate(180deg);
15+
}

shared/common-bricks/common-bricks.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"eo-dropdown-select",
4141
"eo-loading-container",
4242
"eo-viewport",
43-
"eo-list"
43+
"eo-list",
44+
"eo-toggle-link"
4445
],
4546
"icons": [
4647
"eo-antd-icon",

0 commit comments

Comments
 (0)