ConfigBlock.stories.tsx 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import type { Meta, StoryObj } from '@storybook/react-vite';
  2. import { expect, waitFor } from 'storybook/test';
  3. import ConfigBlock from './ConfigBlock';
  4. const meta = {
  5. title: 'Clients/ConfigBlock',
  6. component: ConfigBlock,
  7. tags: ['autodocs'],
  8. parameters: {
  9. layout: 'padded',
  10. docs: {
  11. description: {
  12. component:
  13. 'Collapsible panel that displays a client config or share link with copy, download, and QR-code actions. Used on the clients and inbounds pages to present generated links.',
  14. },
  15. },
  16. },
  17. argTypes: {
  18. label: {
  19. description: 'Protocol/type badge shown on the panel header (e.g. `vless`, `trojan`).',
  20. },
  21. text: {
  22. description:
  23. 'The config or share-link text to display, copy, download, and encode as a QR code.',
  24. },
  25. fileName: { description: 'File name used when downloading the text.' },
  26. qrRemark: { description: 'Optional remark embedded in the QR panel; falls back to `label`.' },
  27. showQr: { description: 'Whether to show the QR-code action button.' },
  28. tagColor: { description: 'Ant Design tag color for the header badge.' },
  29. defaultOpen: { description: 'Whether the panel starts expanded.' },
  30. },
  31. } satisfies Meta<typeof ConfigBlock>;
  32. export default meta;
  33. type Story = StoryObj<typeof meta>;
  34. const sampleLink =
  35. 'vless://[email protected]:443' +
  36. '?type=ws&security=tls&path=%2Fpath#example-node';
  37. export const Collapsed: Story = {
  38. args: { label: 'vless', text: sampleLink, fileName: 'client-config.txt' },
  39. play: async ({ canvas, canvasElement, userEvent }) => {
  40. await expect(canvas.queryByText(/vless:\/\/11112222/)).not.toBeInTheDocument();
  41. await userEvent.click(canvas.getByText('vless'));
  42. const configText = await canvas.findByText(/vless:\/\/11112222/);
  43. await waitFor(() => expect(configText).toBeVisible());
  44. // Collapse fades content in over motionDurationMid; wait it out so the a11y
  45. // scan doesn't sample a mid-transition, lower-contrast opacity.
  46. await waitFor(() => {
  47. const panel = canvasElement.querySelector('.ant-collapse-panel');
  48. expect(panel && getComputedStyle(panel).opacity).toBe('1');
  49. });
  50. await expect(canvas.getByRole('button', { name: 'Copy' })).toBeVisible();
  51. await expect(canvas.getByRole('button', { name: 'Download' })).toBeVisible();
  52. await expect(canvas.getByRole('button', { name: 'QR Code' })).toBeVisible();
  53. },
  54. };
  55. export const Expanded: Story = {
  56. args: { label: 'vless', text: sampleLink, fileName: 'client-config.txt', defaultOpen: true },
  57. };
  58. export const WithoutQr: Story = {
  59. args: {
  60. label: 'trojan',
  61. text: sampleLink,
  62. fileName: 'client-config.txt',
  63. showQr: false,
  64. tagColor: 'geekblue',
  65. },
  66. };