Skip to content
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

fix(tabber): change store key & preserve query params when switching tabbers #5500

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

dingdayu
Copy link
Contributor

@dingdayu dingdayu commented Feb 8, 2025

Description

This update introduces breaking changes, so please review carefully.

Changes:

  • Changed tabber store key from fullPath to path to improve tabber behavior and resolve #5434.
  • Added route.query watch to tabber to prevent loss of URL query parameters when switching tabbers, fixing #5427.

Reason:

Using param and query allows for more flexible tabber uniqueness:

  • When different parameters should be stored in the same tabber, use query.
  • When different parameters should be stored in separate tabbers, use param.

Impact:

  • Existing tabber records will be affected due to the change in store key.
  • Query parameters will now persist across tabber switches, enabling more flexible parameter passing.

Implementation Example:

With this update, table parameters can be passed via URL query, preventing data loss when switching tabbers.

const route = useRoute();
const router = useRouter();

const formOptions: VbenFormProps = {
  schema: [
    { field: 'title', label: 'Title' },
    {
      field: 'status',
      label: 'Status',
      component: 'Select',
      componentProps: {
        options: [
          { label: 'Published', value: 'published' },
          { label: 'Draft', value: 'draft' },
        ],
      },
    },
    {
      field: 'stage',
      label: 'Stage',
      component: 'Select',
      componentProps: {
        options: [
          { label: 'Review', value: 'review' },
          { label: 'Draft', value: 'draft' },
        ],
      },
    },
  ],
  submitOnEnter: true,
  submitOnChange: false,
  handleSubmit: (values) => {
    const newQuery = { ...route.query };
    Object.keys(values).forEach((key) => {
      if (values[key]) {
        newQuery[key] = values[key];
      } else {
        delete newQuery[key];
      }
    });
    router.replace({ query: newQuery });
  },
};

onMounted(() => {
  const query = route.query;
  const formApi = gridApi.formApi;

  query.title && formApi.setFieldValue('title', query.title);
  query.status && formApi.setFieldValue('status', query.status);
  query.stage && formApi.setFieldValue('stage', query.stage);
});

These changes improve tabber navigation consistency and ensure that table filters persist when switching tabs.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

ℹ️ Check all checkboxes - this will indicate that you have done everything in accordance with the rules in CONTRIBUTING.

  • If you introduce new functionality, document it. You can run documentation with pnpm run docs:dev command.
  • Run the tests with pnpm test.
  • Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with feat:, fix:, perf:, docs:, or chore:.
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Enhanced tab management now updates in response to changes in both the URL path and query parameters, resulting in a more dynamic navigation experience.

  • Bug Fixes
    • Improved tab navigation consistency by streamlining how tab paths are determined, ensuring smoother and more accurate tab displays.
    • Adjusted key assignment logic for tabs to prioritize path over fullPath, enhancing unique identification within components.

This change modifies the tabber store key from `fullPath` to `path`, impacting existing tabber records.

### Reason:
Using `param` and `query` allows for more flexible tabber uniqueness:
- When different parameters should be stored in the same tabber, use `query`.
- When different parameters should be stored in separate tabbers, use `param`.
Copy link

changeset-bot bot commented Feb 8, 2025

⚠️ No Changeset found

Latest commit: 5051b18

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Feb 8, 2025

Walkthrough

This pull request modifies the tab management system by enhancing the useTabbar function to observe both route.path and route.query, allowing for dynamic updates. The getTabPath function in the tabbarStore module has been simplified to consistently use tab.path instead of conditionally checking for fullPath. Additionally, the logic for determining the key property in tab components has been updated to prioritize path. These changes aim to stabilize tab creation and highlighting in scenarios with dynamic routing.

Changes

File(s) Change Summary
packages/effects/.../use-tabbar.ts Updated the watch dependency to observe both route.path and route.query; callback now triggers on changes in either.
packages/stores/.../tabbar.ts Simplified getTabPath by removing references to fullPath; now consistently returns the decoded value of tab.path.
packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue Modified key assignment in tabsView to prioritize path over fullPath.
packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue Changed key assignment in tab objects to prioritize path over fullPath.
packages/stores/.../tabbar.test.ts Updated test case to use a variable keyToClose for the path property instead of a hardcoded string.

Assessment against linked issues

Objective Addressed Explanation
Prevent duplicate tab creation and incorrect highlight when dynamic params change (#5434)

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • vince292007
  • anncwb

Poem

I'm a rabbit hopping through lines of code,
Adjusting watchers down the winding road.
Query and path now dance in tune,
Keeping tabs in line like a bright full moon.
Carrots and commits, a joyful spree,
CodeRabbit cheers—hop on, follow me!
🥕🐇

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/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue

Oops! Something went wrong! :(

ESLint: 9.19.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/node_modules/@vben/eslint-config/dist/index.mjs' imported from /eslint.config.mjs
at finalizeResolution (node:internal/modules/esm/resolve:257:11)
at moduleResolve (node:internal/modules/esm/resolve:914:10)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue

Oops! Something went wrong! :(

ESLint: 9.19.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/node_modules/@vben/eslint-config/dist/index.mjs' imported from /eslint.config.mjs
at finalizeResolution (node:internal/modules/esm/resolve:257:11)
at moduleResolve (node:internal/modules/esm/resolve:914:10)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

packages/effects/layouts/src/basic/tabbar/use-tabbar.ts

Oops! Something went wrong! :(

ESLint: 9.19.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/node_modules/@vben/eslint-config/dist/index.mjs' imported from /eslint.config.mjs
at finalizeResolution (node:internal/modules/esm/resolve:257:11)
at moduleResolve (node:internal/modules/esm/resolve:914:10)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 2 others
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

Copy link
Contributor

@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: 1

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5262233 and f8f30b1.

📒 Files selected for processing (2)
  • packages/effects/layouts/src/basic/tabbar/use-tabbar.ts (1 hunks)
  • packages/stores/src/modules/tabbar.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Test (windows-latest)
  • GitHub Check: Check (windows-latest)
  • GitHub Check: Test (ubuntu-latest)
  • GitHub Check: Lint (windows-latest)
  • GitHub Check: Check (ubuntu-latest)
  • GitHub Check: Lint (ubuntu-latest)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: post-update (windows-latest)
  • GitHub Check: post-update (ubuntu-latest)
🔇 Additional comments (1)
packages/effects/layouts/src/basic/tabbar/use-tabbar.ts (1)

102-112: LGTM! Improved tab state management by watching both path and query.

The change to watch both route.path and route.query ensures that tabs are properly updated when either the path or query parameters change, improving the preservation of tab state.

Comment on lines 557 to 559
function getTabPath(tab: RouteRecordNormalized | TabDefinition) {
return decodeURIComponent((tab as TabDefinition).fullPath || tab.path);
return decodeURIComponent(tab.path);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 8, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

LGTM! Simplified tab path resolution.

The change to consistently use tab.path instead of fullPath makes the tab path resolution more predictable. However, since this is a breaking change:

Please verify the impact by checking existing tab state persistence:


🏁 Script executed:

#!/bin/bash
# Description: Check for any remaining uses of fullPath in tab-related code
# that might be affected by this change

echo "Checking for remaining fullPath usage in tab-related code..."
rg -l 'fullPath.*tab' 

echo "Checking for direct tab.fullPath access..."
ast-grep --pattern 'tab.fullPath'

Length of output: 834


Attention: Breaking Change Impact Detected

The update to use tab.path in the getTabPath function is not fully propagated across the codebase. Our automated checks revealed several remaining references to tab.fullPath which could lead to inconsistencies or runtime errors. Specifically:

  • Files referencing tab.fullPath:
    • packages/stores/src/modules/tabbar.ts:
      • Line 200: if (path !== tab.fullPath) { … }
      • Line 302: openRouteInNewWindow(tab.fullPath || tab.path);
    • packages/stores/src/modules/tabbar.test.ts:
      • Line 276: Using tab.fullPath in a lookup.
    • Other components using tab.fullPath:
      • packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue
      • packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue
      • packages/effects/layouts/src/basic/tabbar/use-tabbar.ts:149 (used with router.push)

These findings confirm a breaking change impact that needs careful attention. Please review and update these references to ensure consistency across the system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packages/effects/layouts/src/basic/tabbar/use-tabbar.ts:149packages/stores/src/modules/tabbar.ts:302

      {
        handler: async () => {
          if (!contentIsMaximize.value) {
            await router.push(tab.fullPath);
          }
          toggleMaximize();
        },
        icon: contentIsMaximize.value ? Minimize2 : Fullscreen,
        key: contentIsMaximize.value ? 'restore-maximize' : 'maximize',
        text: contentIsMaximize.value
          ? $t('preferences.tabbar.contextMenu.restoreMaximize')
          : $t('preferences.tabbar.contextMenu.maximize'),
      }
    /**
     * @zh_CN 新窗口打开标签页
     * @param tab
     */
    async openTabInNewWindow(tab: TabDefinition) {
      openRouteInNewWindow(tab.fullPath || tab.path);
    },

I think it is more reasonable to continue using fullPath in the two places mentioned above.

Copy link
Contributor

Choose a reason for hiding this comment

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

@dingdayu Agreed! You make a valid point. Using fullPath in these specific cases is indeed more appropriate because:

  1. For router navigation (router.push), we want to preserve the complete URL including query parameters
  2. For opening in new window, we need the full URL to maintain the exact state

✏️ Learnings added
Learnt from: dingdayu
PR: vbenjs/vue-vben-admin#5500
File: packages/stores/src/modules/tabbar.ts:557-559
Timestamp: 2025-02-08T07:05:28.553Z
Learning: In Vue Vben Admin, while tab identification uses `path` to group tabs with same path but different queries, router navigation operations (router.push) and new window operations should use `fullPath` to preserve the complete URL including query parameters. This applies specifically to:
1. Router navigation in tab maximize/restore feature
2. Opening tabs in new window via openTabInNewWindow

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@dingdayu dingdayu marked this pull request as draft February 8, 2025 06:32
Resolved an issue where tabs could not be closed due to the recent change in the tabber store key.
@dingdayu dingdayu marked this pull request as ready for review February 8, 2025 07:05
Copy link
Contributor

@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: 1

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f8f30b1 and 5051b18.

📒 Files selected for processing (5)
  • packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue (1 hunks)
  • packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue (1 hunks)
  • packages/effects/layouts/src/basic/tabbar/use-tabbar.ts (2 hunks)
  • packages/stores/src/modules/tabbar.test.ts (1 hunks)
  • packages/stores/src/modules/tabbar.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/effects/layouts/src/basic/tabbar/use-tabbar.ts
  • packages/stores/src/modules/tabbar.ts
🔇 Additional comments (2)
packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue (1)

57-57: LGTM! Change aligns with PR objectives.

The modification to use path as the primary key with fullPath as fallback aligns with the PR's goal of changing the store key from fullPath to path. This change helps preserve query parameters while maintaining tab uniqueness.

To ensure this change doesn't break existing functionality, verify that:

  1. Tabs with the same path but different query parameters are handled correctly
  2. No duplicate tabs are created when switching between routes
✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for any other components that might be affected by this change
rg -A 5 "fullPath.*path|path.*fullPath" --type vue

Length of output: 80


🏁 Script executed:

#!/bin/bash
# Re-run the search ensuring .vue files are recognized by ripgrep.
# This command manually sets the 'vue' file type to *.vue files.
rg --type-add 'vue:*.vue' --type vue -A 5 "key: path \|\| fullPath" packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue

Length of output: 268


Verification Completed: Tab Key Implementation is Correct

• The snippet in packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue shows that the key is now defined as “key: path || fullPath”, which aligns with the PR’s objective of preferring “path” (to ensure no duplicate tabs for the same base route) while still falling back to “fullPath” when necessary.
• This change guarantees that even if a route’s query parameters differ, the tab’s uniqueness is maintained by the consistent “path” key.
• There’s no evidence found in the code that this change could inadvertently create duplicate tabs when switching between routes.

packages/stores/src/modules/tabbar.test.ts (1)

263-263: LGTM! Test improvement using variable.

Using the keyToClose variable instead of a hardcoded path makes the test more maintainable and ensures consistency between the test setup and the actual value being tested.

@@ -48,7 +48,7 @@ const tabsView = computed(() => {
closable: Reflect.has(meta, 'tabClosable') ? !!tabClosable : true,
fullPath,
icon: icon as string,
key: fullPath || path,
key: path || fullPath,
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 8, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Breaking Change: Tab key prioritization changed from fullPath to path.

This change aligns with the PR objectives to use path as the store key. However, as this is a breaking change that affects tab identification, please ensure:

  1. The change is documented in the changelog
  2. A migration guide is provided for users
  3. Users are warned about potential tab state reset

Please verify the documentation by checking:


🏁 Script executed:

#!/bin/bash
# Search for documentation updates in changelog and migration guides
rg -i "changelog|migration|breaking.*change" -g "*.md"

Length of output: 1756


Breaking Change Documentation Update Required

The refactor that switches the tab key determination from using “fullPath” to “path” meets the PR objectives. However, the shell search for documentation only yielded generic changelog and changeset references—with no explicit migration notes addressing this breaking change. This could leave users unprepared for potential tab state resets or key duplications.

• Verify that the changelog (and any migration guide) is updated with explicit details about the tab key change and its impact.
• Provide clear migration instructions that alert users to the possibility of tab state resets or duplications, along with any recommended corrective actions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, CHANGELOG is generated by changesets, I don't know how to update changlog.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for changeset configuration
fd -e json -e yaml -e yml "changeset" 
# Check for existing changeset files in .changeset directory
fd . ".changeset" -t f

Length of output: 104


🏁 Script executed:

#!/bin/bash
# Check changeset configuration
cat .changeset/config.json
# Check changeset README for any project-specific instructions
cat .changeset/README.md

Length of output: 1038


@dingdayu Thanks for letting me know! Since the project uses changesets, you can document this breaking change by creating a new changeset. Here's how:

  1. Run the following command in your terminal:
pnpm changeset
  1. When prompted:
    • Select all the packages that are affected (@vben-core/ui-kit, @vben/stores)
    • Choose "major" as the bump type since this is a breaking change
    • Provide a description like this:
BREAKING CHANGE: Changed tab key prioritization from `fullPath` to `path`

This change affects how tabs are identified in the system. Previously, tabs were primarily identified by their `fullPath`, with `path` as a fallback. Now, `path` is used as the primary identifier with `fullPath` as fallback.

Impact:
- Existing tab states may be reset
- Tab uniqueness behavior has changed
- Applications relying on `fullPath` for tab identification need to be updated

The changeset will be automatically included in the CHANGELOG when you create a release.

(`・ω・´)ゞ


✏️ Learnings added
Learnt from: dingdayu
PR: vbenjs/vue-vben-admin#5500
File: packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue:51-51
Timestamp: 2025-02-08T07:36:14.571Z
Learning: The project uses changesets for version management and changelog generation. Breaking changes should be documented by creating a new changeset using `pnpm changeset` command, selecting affected packages, choosing "major" as the bump type, and providing a detailed description of the change and its impact.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@dingdayu
Copy link
Contributor Author

@mynetfan Any comments on this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: 关于动态路由的params和query的问题
1 participant