Skip to content

feat(grid): add expand trigger slot #3518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 23, 2025
Merged

feat(grid): add expand trigger slot #3518

merged 3 commits into from
Jun 23, 2025

Conversation

gimmyhehe
Copy link
Member

@gimmyhehe gimmyhehe commented Jun 17, 2025

PR

新增expand-trigger插槽

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Added support for customizing the expand row icon in the grid component using a new expand-trigger slot.
    • Introduced new demo examples showcasing the expand-trigger slot and its usage in data grids.
    • Extended grid column configuration options with additional properties such as id, rules, required, width, minWidth, resizable, and enhanced fixed options.
  • Bug Fixes

    • Improved type definitions for cell class name functions to provide more detailed context.
  • Tests

    • Added automated tests to verify the expand row trigger slot functionality and its UI behavior.

Copy link

coderabbitai bot commented Jun 17, 2025

Walkthrough

The changes introduce a customizable expand row trigger for a grid component by adding an expand-trigger slot, updating type definitions, and providing new Vue demo components and tests. The grid's column configuration and cell class name argument types are extended for more detailed customization and validation options.

Changes

File(s) Change Summary
examples/sites/demos/apis/grid.js Updated grid API documentation and types: new expand-trigger slot, revised IColumnConfig interface, added ICellClassNameArgs type, and updated property types and comments.
packages/vue/src/grid/src/cell/src/cell.ts Modified renderExpandCell to support the expand-trigger slot, allowing custom expand icon content via slot function.
examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.vue
...composition-api.vue
Added new Vue demo components showing usage of the expand-trigger slot for custom expand/collapse buttons and expanded row content, with event handling for row expansion.
examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.spec.ts Added Playwright test to verify expand row trigger functionality and correct display of expanded content.
examples/sites/demos/pc/app/grid/webdoc/grid-expand.js Added a new demo entry for the expand-trigger slot usage in the grid expand documentation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GridComponent
    participant ColumnConfig
    participant CustomSlot

    User->>GridComponent: Clicks expand trigger
    GridComponent->>ColumnConfig: Checks for 'expand-trigger' slot
    alt Slot exists
        ColumnConfig->>CustomSlot: Render custom expand trigger (with params)
        CustomSlot-->>GridComponent: Custom trigger content
    else No slot
        GridComponent-->>User: Render default expand icon
    end
    GridComponent->>User: Toggle row expansion and show expanded content
Loading

Poem

In the grid where data flows and grows,
A bunny hops and custom icons show—
Expand or collapse, with a slot so neat,
Now every row’s reveal is a treat!
With types refined and tests in tow,
The grid just got a brand-new glow.
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/vue/src/grid/src/cell/src/cell.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-vue".

(The package "eslint-plugin-vue" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-vue@latest --save-dev

The plugin "eslint-plugin-vue" was referenced from the config file in ".eslintrc.js » @antfu/eslint-config » @antfu/eslint-config-vue".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d514a0b and 1c44bdb.

📒 Files selected for processing (1)
  • packages/vue/src/grid/src/cell/src/cell.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/vue/src/grid/src/cell/src/cell.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: PR E2E Test (pnpm test:e2e3)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the enhancement New feature or request (功能增强) label Jun 17, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
examples/sites/demos/apis/grid.js (1)

3356-3361: Typo breaks TS literal union

cell is missing quotes.

-  trigger?: 'default' | 'row' | cell
+  trigger?: 'default' | 'row' | 'cell'

This currently makes the whole examples/sites/demos/apis/grid.js file invalid TypeScript.

♻️ Duplicate comments (1)
examples/sites/demos/pc/app/grid/expand/expand-trigger-slot-composition-api.vue (1)

77-84: Same JSON.stringify issue as the options-API demo

See previous comment – dropping JSON.stringify simplifies the code.

🧹 Nitpick comments (6)
examples/sites/demos/pc/app/grid/webdoc/grid-expand.js (1)

18-28: Document both demo variants & tighten EN description

There is a Composition-API variant (expand-trigger-slot-composition-api.vue) that is currently undiscoverable from the docs array. Also, the EN description is missing the article “an”.

-      desc: {
-        'zh-CN': `
-        <p>通过 <code>expand-trigger</code> 插槽可以自定义展开行图标。</p>
-        `,
-        'en-US': '<p>You can customize the expand row icon through the <code>expand-trigger</code> slot. </p>\n'
-      },
-      codeFiles: ['expand/expand-trigger-slot.vue']
+      desc: {
+        'zh-CN': '<p>通过 <code>expand-trigger</code> 插槽可以自定义展开行图标。</p>',
+        'en-US': '<p>You can customise an expand-row icon through the <code>expand-trigger</code> slot.</p>'
+      },
+      codeFiles: [
+        'expand/expand-trigger-slot.vue',
+        'expand/expand-trigger-slot-composition-api.vue'
+      ]
packages/vue/src/grid/src/cell/src/cell.ts (1)

655-666: Nit: misleading variable name & single-state icon

hideExpand actually means “row is expandable”. Renaming would improve readability:

-    let hideExpand = typeof expandMethod === 'function' ? expandMethod(row) : true
+    const isExpandable = typeof expandMethod === 'function' ? expandMethod(row) : true

(remember to update the two usages below).

Additionally, the fallback icon <i class="tiny-grid__expand-icon"> is static; it does not reflect the expandActive state. Consider toggling a modifier class or re-using iconExpand/iconPutAway for visual consistency when no slot is supplied.

examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.vue (2)

6-8: Minor: use computed label instead of inline ternary

For clarity and reusability:

-          <tiny-button>{{ $table.hasRowExpand(row) ? '收起' : '展开' }}</tiny-button>
+          <tiny-button>{{ $table.hasRowExpand(row) ? '收起' : '展开' }}</tiny-button>

becomes

<template #expand-trigger="{ row, $table }">
  <tiny-button>{{ getTriggerLabel(row, $table) }}</tiny-button>
</template>

with

methods: {
  getTriggerLabel(row, $table) {
    return $table.hasRowExpand(row) ? '收起' : '展开'
  },}

Keeps the template lean and avoids duplicating the ternary in the composition-api demo.


85-92: Avoid JSON.stringify for plain numbers

rowIndex + 1 is an integer; wrapping it with JSON.stringify is unnecessary:

-          message: `当前展开行:${JSON.stringify(rowIndex + 1)}`,
+          message: `当前展开行:${rowIndex + 1}`,
examples/sites/demos/apis/grid.js (2)

2970-2983: Slot added but demo link is 404-prone

Great to see expand-trigger exposed!
Small follow-ups to avoid broken docs:

-  pcDemo: 'grid-expand#expand-trigger-slot'
+  pcDemo: 'grid-expand#expand-trigger-slot'      // ✅ Make sure this anchor exists in the md/vue demo

If the demo file/anchor doesn’t exist yet, the docs site will render an empty link.


4178-4200: ICellClassNameArgs duplicates much of IClassNameArgs

The new interface just adds level, data and splits column indices. Consider extending instead of duplicating to avoid drift:

-interface ICellClassNameArgs { /* … all fields … */ }
+interface ICellClassNameArgs extends IClassNameArgs {
+  level: number
+  data: IRow[]
+}

Keeps maintenance simpler while still delivering the extra metadata.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a14507a and d514a0b.

📒 Files selected for processing (6)
  • examples/sites/demos/apis/grid.js (4 hunks)
  • examples/sites/demos/pc/app/grid/expand/expand-trigger-slot-composition-api.vue (1 hunks)
  • examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.spec.ts (1 hunks)
  • examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.vue (1 hunks)
  • examples/sites/demos/pc/app/grid/webdoc/grid-expand.js (1 hunks)
  • packages/vue/src/grid/src/cell/src/cell.ts (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: PR E2E Test (pnpm test:e2e3)
🔇 Additional comments (2)
packages/vue/src/grid/src/cell/src/cell.ts (1)

690-691: Guard against empty trigger slot

[isExpandable && triggerContent] renders false when the slot returns undefined, producing a stray comment node in Vue. Wrap with triggerContent &&:

-        [isExpandable && triggerContent]
+        [isExpandable && triggerContent && triggerContent]

Likely an incorrect or invalid review comment.

examples/sites/demos/apis/grid.js (1)

59-62: Anchor change looks good – double-check downstream tooling

typeAnchorName switched to ICellClassNameArgs and the function signature now uses the same type.
All good as long as:

  1. The new interface is really exported (it is, see ~4178).
  2. Doc-site generators & IDE plugins know about the new anchor.

No action required if those pipelines already pass.

Comment on lines +4 to +10
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-expand#expand-trigger-slot')
await page.locator('#expand-trigger-slot .tiny-grid-body__row').first().locator('.tiny-button').click()
await expect(page.locator('div').filter({ hasText: '当前展开行:1' }).nth(1)).toBeVisible()
await expect(page.locator('.tiny-grid-body__expanded-cell')).toHaveText(
'公司名称:GFD 科技 YX 公司区域:华东区员工数:800公司简介:公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

❓ Verification inconclusive

Make the assertions less brittle

  • nth(1) assumes there are exactly two matching nodes; the test will silently pass even if an unrelated element slips in ahead of the expected one.
  • toHaveText(...) with the full Chinese string will fail on any extra whitespace or line-breaks inserted by i18n tooling or prettifiers.
-  await expect(page.locator('div').filter({ hasText: '当前展开行:1' }).nth(1)).toBeVisible()
-  await expect(page.locator('.tiny-grid-body__expanded-cell')).toHaveText(
+  await expect(page.locator('div:has-text("当前展开行:1")').first()).toBeVisible()
+  await expect(
+    page.locator('.tiny-grid-body__expanded-cell')
+  ).toContainText('公司名称:GFD 科技 YX 公司')

The revised selectors are unambiguous and the partial-text assertion guards against harmless formatting changes.


Make the assertions less brittle

  • nth(1) assumes there are exactly two matching nodes; the test will silently pass even if an unrelated element slips in ahead of the expected one.
  • toHaveText(...) with the full Chinese string will fail on any extra whitespace or line-breaks inserted by i18n tooling or prettifiers.
-  await expect(page.locator('div').filter({ hasText: '当前展开行:1' }).nth(1)).toBeVisible()
-  await expect(page.locator('.tiny-grid-body__expanded-cell')).toHaveText(
+  await expect(page.locator('div:has-text("当前展开行:1")').first()).toBeVisible()
+  await expect(
+    page.locator('.tiny-grid-body__expanded-cell')
+  ).toContainText('公司名称:GFD 科技 YX 公司')

The revised selectors are unambiguous and the partial-text assertion guards against harmless formatting changes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-expand#expand-trigger-slot')
await page.locator('#expand-trigger-slot .tiny-grid-body__row').first().locator('.tiny-button').click()
await expect(page.locator('div').filter({ hasText: '当前展开行:1' }).nth(1)).toBeVisible()
await expect(page.locator('.tiny-grid-body__expanded-cell')).toHaveText(
'公司名称:GFD 科技 YX 公司区域:华东区员工数:800公司简介:公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”。'
)
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('grid-expand#expand-trigger-slot')
await page.locator('#expand-trigger-slot .tiny-grid-body__row').first().locator('.tiny-button').click()
await expect(page.locator('div:has-text("当前展开行:1")').first()).toBeVisible()
await expect(
page.locator('.tiny-grid-body__expanded-cell')
).toContainText('公司名称:GFD 科技 YX 公司')
🤖 Prompt for AI Agents
In examples/sites/demos/pc/app/grid/expand/expand-trigger-slot.spec.ts around
lines 4 to 10, the test assertions are brittle because nth(1) assumes exactly
two matching elements and toHaveText uses a full Chinese string that can fail
due to whitespace or formatting changes. To fix this, update the selector to
target the specific element unambiguously without relying on nth(1), and replace
the full text assertion with a partial text match that ignores minor formatting
differences, making the test more robust.

Comment on lines +3211 to 3229
// 功能列的类型, 'index'行索引,'radio' 单选行, 'selection' 多选行
type: 'index' | 'radio' | 'selection'
// 列id
id: string
prop: string
// 校验规则
rules: IValidRules
// 是否必填
required: boolean
property: string
title: string
label: string
// 列宽度
width: string | number
// 自动分配宽度时的最小宽度
minWidth: string | number
// 是否可以调整列宽
resizable: boolean
fixed: boolean
// 是否左、右冻结
fixed: 'left' | 'right'
align: 'left' | 'center' | 'right'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

IColumnConfig makes formerly-optional props mandatory & drops 'expand'

  1. The union now omits 'expand', while the prop table above (2921-2924) still advertises it.
  2. id, rules, required, width, minWidth, resizable, fixed are all non-optional – this is a breaking change for every existing consumer.
-  type: 'index' | 'radio' | 'selection'
+  type?: 'index' | 'radio' | 'selection' | 'expand'

-  id: string
-  rules: IValidRules
-  required: boolean
+  id?: string
+  rules?: IValidRules
+  required?: boolean
   ...
-  width: string | number
-  minWidth: string | number
-  resizable: boolean
-  fixed: 'left' | 'right'
+  width?: string | number
+  minWidth?: string | number
+  resizable?: boolean
+  fixed?: 'left' | 'right'

Making them optional preserves backwards compatibility and syncs the type set with the public prop docs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 功能列的类型, 'index'行索引,'radio' 单选行, 'selection' 多选行
type: 'index' | 'radio' | 'selection'
// 列id
id: string
prop: string
// 校验规则
rules: IValidRules
// 是否必填
required: boolean
property: string
title: string
label: string
// 列宽度
width: string | number
// 自动分配宽度时的最小宽度
minWidth: string | number
// 是否可以调整列宽
resizable: boolean
fixed: boolean
// 是否左、右冻结
fixed: 'left' | 'right'
align: 'left' | 'center' | 'right'
// 功能列的类型, 'index'行索引,'radio' 单选行, 'selection' 多选行
type?: 'index' | 'radio' | 'selection' | 'expand'
// 列id
id?: string
// 校验规则
rules?: IValidRules
// 是否必填
required?: boolean
property: string
title: string
// 列宽度
width?: string | number
// 自动分配宽度时的最小宽度
minWidth?: string | number
// 是否可以调整列宽
resizable?: boolean
// 是否左、右冻结
fixed?: 'left' | 'right'
align: 'left' | 'center' | 'right'
🤖 Prompt for AI Agents
In examples/sites/demos/apis/grid.js around lines 3211 to 3229, the
IColumnConfig type incorrectly makes several previously optional properties
mandatory and removes the 'expand' type, causing breaking changes and
inconsistency with the documented props. To fix this, revert these properties
(id, rules, required, width, minWidth, resizable, fixed) to be optional by
adding '?' to their declarations, and re-include 'expand' in the union type to
align with the public prop documentation and maintain backward compatibility.

@zzcr zzcr merged commit 25bf285 into dev Jun 23, 2025
9 of 10 checks passed
@zzcr zzcr deleted the cgm/add-expand-trigger-slot branch June 23, 2025 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request (功能增强)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants