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

Props hoc #3084

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

Props hoc #3084

wants to merge 3 commits into from

Conversation

hashiqi12138
Copy link
Contributor

@hashiqi12138 hashiqi12138 commented Mar 8, 2025

PR

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?

为 vue3及 vue2 增加 WithDesignConfigPropsDefineComponent 方法

组件中引入 defineComponent 的地方改为 WithDesignConfigPropsDefineComponent
import { $props, $prefix, $setup, WithDesignConfigPropsDefineComponent as defineComponent } from '@opentiny/vue-common'

目前仅 Button 进行了调整,Vue2,Vue3中都达到了预期的效果,Button 组件Vue3版本单元测试也能正常通过

Issue Number: #2940

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Enhanced UI components now support global design configuration, offering improved customization and consistent styling—especially noticeable in button behaviors.
  • Tests

    • Upgraded the unit testing experience with an interactive testing interface for more intuitive validation.

Copy link

coderabbitai bot commented Mar 8, 2025

Walkthrough

This pull request introduces design configuration enhancements across components. The button demo now wraps <tiny-button> components within a new <config-provider> and <tiny-row> and defines a design data property. The unit test command in package.json is updated to use vitest --ui, with an upgraded dependency. Additionally, new Higher-Order Components (HOC) and helper functions—DesignConfigPropsHOC and WithDesignConfigPropsDefineComponent—are added to both Vue 2 and Vue 3 adapter modules for integrating global design configurations. The button component import is updated to use the new HOC.

Changes

File(s) Changes
examples/.../button/basic-usage.vue Wrapped <tiny-button> components with <config-provider> inside <tiny-row>; added design data property (with resetTime: 0, size: 'mini').
examples/vue3/package.json Updated test script from "vitest" to "vitest --ui" and upgraded @vitest/ui from ^0.31.0 to ^2.1.9.
packages/vue-common/src/adapter/vue{2,2.7,3}/*
packages/vue-common/src/index.ts
Added new HOC DesignConfigPropsHOC and helper function WithDesignConfigPropsDefineComponent for injecting global design configurations; updated module exports accordingly.
packages/vue/src/button/src/index.ts Replaced the direct import of defineComponent with WithDesignConfigPropsDefineComponent as defineComponent to incorporate design configuration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CP as ConfigProvider
    participant TR as TinyRow
    participant TB as TinyButton

    User->>CP: Initiate button interaction
    CP->>TR: Wraps TinyButton(s)
    TR->>TB: Supplies design settings (resetTime=0, size='mini')
    TB->>User: Renders configured button
Loading
sequenceDiagram
    participant BC as BaseComponent
    participant HOC as WithDesignConfigPropsDefineComponent
    participant GC as GlobalDesignConfig
    participant Render as EnhancedComponent

    BC->>HOC: Wrapped by HOC
    HOC->>GC: Inject global design config
    GC-->>HOC: Returns design properties
    HOC->>Render: Merge props and render component
    Render->>User: Display enhanced component
Loading

Suggested labels

enhancement

Suggested reviewers

  • zzcr

Poem

I'm hopping through lines of code so bright,
With buttons wrapped in config light.
HOCs and functions join the parade,
In Vue's realm, fresh changes are made.
CodeRabbit hops with glee and cheer,
Celebrating each update far and near!
🐰✨

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-common/src/adapter/vue2.7/hoc.tsx

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.

packages/vue-common/src/adapter/vue2.7/index.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.

examples/sites/demos/pc/app/button/basic-usage.vue

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.

  • 6 others
✨ 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.
    • 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.
  • @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

Walkthrough

This PR introduces a new feature by adding the WithDesignConfigPropsDefineComponent method for both Vue 2 and Vue 3. It replaces instances of defineComponent with WithDesignConfigPropsDefineComponent in components, specifically adjusting the Button component to achieve the desired effect in both Vue 2 and Vue 3 environments.

Changes

Files Summary
examples/sites/demos/pc/app/button/basic-usage.vue Wrapped tiny-button components with config-provider and added design configuration.
packages/vue-common/src/adapter/vue2.7/hoc.tsx
packages/vue-common/src/adapter/vue2/hoc.tsx
packages/vue-common/src/adapter/vue3/hoc.tsx
Added DesignConfigPropsHOC function to inject design configuration into components.
packages/vue-common/src/adapter/vue2.7/index.ts
packages/vue-common/src/adapter/vue2/index.ts
packages/vue-common/src/adapter/vue3/index.ts
Introduced WithDesignConfigPropsDefineComponent function to wrap components with design configuration.
packages/vue-common/src/index.ts Exported WithDesignConfigPropsDefineComponent from adapter.
packages/vue/src/button/src/index.ts Replaced defineComponent with WithDesignConfigPropsDefineComponent.

methods: {
// 解包可能存在的响应式对象
unwrap(config) {
if (config && typeof config === 'object' && 'value' in config) {

Choose a reason for hiding this comment

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

Ensure that the unwrap method correctly handles all possible types of reactive objects to avoid unexpected behavior.

Copy link

github-actions bot commented Mar 8, 2025

[e2e-test-warn]
The component to be tested is missing.

The title of the Pull request should look like "fix(vue-renderless): [action-menu, alert] fix xxx bug".

Please make sure you've read our contributing guide

methods: {
// 解包可能存在的响应式对象
unwrap(config) {
if (config && typeof config === 'object' && 'value' in config) {

Choose a reason for hiding this comment

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

Ensure that the unwrap method correctly handles all possible types of reactive objects to avoid unexpected behavior.

// 获取组件级配置和全局配置(inject需要带有默认值,否则控制台会报警告)
let globalDesignConfig = hooks.inject(design.configKey, {})
// globalDesignConfig 可能是响应式对象,比如 computed
globalDesignConfig = globalDesignConfig?.value || globalDesignConfig || {}

Choose a reason for hiding this comment

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

Ensure that the globalDesignConfig is correctly unwrapped to handle all possible types of reactive objects to avoid unexpected behavior.

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: 0

🧹 Nitpick comments (3)
packages/vue-common/src/adapter/vue2/hoc.tsx (3)

14-28: Consider adding error handling for method exposure

The method exposure implementation watches for changes to innerRef and copies methods to the wrapper instance. While functional, this approach could be more robust.

Consider adding error handling to prevent potential issues when accessing methods:

created() {
  // 暴露实例方法
  this.$watch(
    () => this.innerRef,
    (val) => {
      if (val) {
        Object.keys(val).forEach((key) => {
          if (!(key in this)) {
+           try {
              this[key] = val[key]
+           } catch (err) {
+             console.warn(`[DesignConfigHOC] Failed to expose method ${key}:`, err)
+           }
          }
        })
      }
    }
  )
}

34-48: Consider adding component name to HOC for better debugging

The HOC name is currently hardcoded as 'DesignConfigHOC', which may make debugging difficult when multiple components are wrapped.

Consider including the base component name in the HOC name:

export default function DesignConfigPropsHOC(BaseComponent) {
+  const componentName = BaseComponent.name || (BaseComponent.options && BaseComponent.options.name) || 'Unknown'
  return Vue.extend({
-    name: 'DesignConfigHOC',
+    name: `DesignConfigHOC(${componentName})`,
    functional: false,
    // rest of the implementation...

50-57: Enhance unwrap method to handle more reactive types

The current implementation may not handle all types of reactive objects in Vue ecosystem.

Consider expanding the unwrap method to handle more types of reactive objects:

unwrap(config) {
  if (config && typeof config === 'object') {
    // Handle Vue 2 reactivity
    if ('value' in config && typeof config.value !== 'undefined') {
      return config.value
    }
+   // Handle ref-like objects
+   if (typeof config.getValue === 'function') {
+     return config.getValue()
+   }
  }
  return config || {}
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cd468ac and 4d9f398.

📒 Files selected for processing (10)
  • examples/sites/demos/pc/app/button/basic-usage.vue (3 hunks)
  • examples/vue3/package.json (3 hunks)
  • packages/vue-common/src/adapter/vue2.7/hoc.tsx (1 hunks)
  • packages/vue-common/src/adapter/vue2.7/index.ts (2 hunks)
  • packages/vue-common/src/adapter/vue2/hoc.tsx (1 hunks)
  • packages/vue-common/src/adapter/vue2/index.ts (2 hunks)
  • packages/vue-common/src/adapter/vue3/hoc.tsx (1 hunks)
  • packages/vue-common/src/adapter/vue3/index.ts (2 hunks)
  • packages/vue-common/src/index.ts (2 hunks)
  • packages/vue/src/button/src/index.ts (1 hunks)
🔇 Additional comments (27)
packages/vue/src/button/src/index.ts (1)

12-12: Import updated to use WithDesignConfigPropsDefineComponent

The import statement has been modified to use the new WithDesignConfigPropsDefineComponent (aliased as defineComponent) instead of the direct defineComponent from @opentiny/vue-common. This change enables the Button component to support global design configurations.

packages/vue-common/src/adapter/vue3/index.ts (2)

16-16: Added DesignConfigPropsHOC import

This import is required for the new WithDesignConfigPropsDefineComponent function.


481-488: Added WithDesignConfigPropsDefineComponent function

This new function implements a Higher-Order Component pattern to enhance components with design configuration capabilities. The implementation correctly wraps the component definition with the DesignConfigPropsHOC after processing it with Vue's defineComponent.

The commented example code also provides good guidance on usage.

examples/sites/demos/pc/app/button/basic-usage.vue (4)

5-12: Added ConfigProvider to demonstrate design prop configuration

The basic buttons section has been wrapped with the new ConfigProvider component, demonstrating how to provide design configuration to button components. This serves as an excellent usage example for the new feature.


45-45: Added ConfigProvider to imports

The ConfigProvider component has been properly imported from the @opentiny/vue package.


52-54: Registered ConfigProvider in components object

ConfigProvider has been correctly added to the components registration.


63-72: Added design configuration to component data

This data property defines the design configuration that will be provided to all Button components within the ConfigProvider. The structure is well-organized with proper nesting for component-specific props.

examples/vue3/package.json (2)

15-15: Updated unit test command to use the UI interface

The unit test command now includes the --ui flag to enable the Vitest UI interface, which provides a better testing experience.


40-40: Updated @vitest/ui dependency to a newer version

The @vitest/ui package has been upgraded from ^0.31.0 to ^2.1.9, which is a significant version bump that provides newer features and bug fixes for the test UI interface.

packages/vue-common/src/adapter/vue2/index.ts (2)

6-6: Import addition looks good

This correctly imports the DesignConfigPropsHOC from the local HOC implementation file.


346-353: Well-structured HOC implementation with good documentation

The implementation of WithDesignConfigPropsDefineComponent follows the correct pattern for a higher-order component that wraps Vue's defineComponent. The commented example provides clear guidance on how to use this new function.

A few observations:

  • The function correctly takes a BaseComponent as input
  • It properly composes the HOC with defineComponent
  • TypeScript typing is correctly applied
  • The example demonstrates the intended use case

This implementation will allow components to receive design configuration properties while maintaining compatibility with Vue's component definition API.

packages/vue-common/src/adapter/vue2.7/index.ts (2)

16-16: Import addition looks good

This correctly imports the DesignConfigPropsHOC from the Vue 2.7-specific HOC implementation.


345-352: Consistent implementation across Vue versions

The implementation of WithDesignConfigPropsDefineComponent for Vue 2.7 matches the Vue 2 implementation, ensuring consistent behavior across Vue versions. The comment example is also helpful for developers.

packages/vue-common/src/adapter/vue3/hoc.tsx (4)

1-4: Appropriate imports for Vue 3 HOC

The imports are well structured:

  • Using Vue 3's type system with SetupContext
  • Importing design utilities and hooks from the common index
  • Importing the helper function getComponentName from the adapter

This sets up the necessary dependencies for the HOC implementation.


5-20: Well-designed global configuration injection

The HOC implementation correctly:

  1. Preserves the original component's properties
  2. Sets up a ref for exposing the inner component
  3. Injects the global design configuration with a default empty object
  4. Handles potentially reactive configuration objects
  5. Retrieves component-specific configuration
  6. Merges configuration props with passed attrs

This approach ensures design configurations are properly injected while maintaining component functionality.


20-33: Effective method exposure through proxy pattern

The implementation uses a Proxy to expose the inner component's methods, which is a clean approach to forward method calls and property access to the wrapped component. This maintains the component's public API.


34-43: Clean rendering implementation for Vue 3

The render function correctly:

  1. Returns a JSX implementation of the BaseComponent
  2. Spreads the merged props to the component
  3. Passes the inner ref for method exposure
  4. Passes through slots

This completes the HOC pattern appropriately for Vue 3's composition API.

packages/vue-common/src/adapter/vue2.7/hoc.tsx (6)

1-3: Appropriate imports for Vue 2.7 HOC

The imports include Vue and the required design utilities while keeping the dependencies minimal.


4-14: Effective dependency injection setup

The HOC implementation correctly:

  1. Uses Vue.extend for Vue 2.7 compatibility
  2. Sets up dependency injection for the design configuration
  3. Provides a default empty object to prevent errors

This approach ensures consistent behavior with Vue 2.7's component model.


14-33: Clean method exposure pattern

The created hook and watcher setup provide an elegant solution for exposing the inner component's methods to the outer component instance. The data initialization for innerRef is appropriately defined.


34-48: Well-designed computed properties

The computed properties correctly:

  1. Extract the component name by removing the prefix
  2. Handle the unwrapping of potentially reactive configuration objects
  3. Merge component-specific props with passed attributes

This ensures the design configuration is properly applied to the component.


49-57: Robust configuration handling

The unwrap method properly handles different types of configuration objects, including reactive objects with a .value property. This ensures compatibility with both direct objects and computed properties.


58-73: Appropriate render implementation for Vue 2.7

The render function correctly:

  1. Creates the BaseComponent with h function
  2. Sets up the ref callback to store the component instance
  3. Passes merged props as attrs
  4. Forwards listeners and slots

This completes the HOC pattern appropriately for Vue 2.7's rendering model.

packages/vue-common/src/index.ts (2)

24-24: LGTM: Import line updated to include WithDesignConfigPropsDefineComponent

The import now includes the new Higher-Order Component for design configuration, which aligns with the PR objective.


40-40: LGTM: Export updated to include WithDesignConfigPropsDefineComponent

The export correctly makes the new HOC available to other components in the application.

packages/vue-common/src/adapter/vue2/hoc.tsx (2)

4-73: Implementation of DesignConfigPropsHOC looks good

This HOC effectively wraps components with design configuration capabilities, allowing for global design props to be merged with component-specific attributes.

The implementation:

  1. Correctly injects the global design configuration
  2. Computes component name by removing the prefix
  3. Merges component-specific design props with passed attributes
  4. Handles exposing inner component methods to the wrapper component
  5. Properly renders the base component with all necessary props and slots

58-71: LGTM: Render function implementation

The render function correctly passes all necessary props, listeners, and slots to the base component while setting up the reference for method exposure.

@@ -9,7 +9,7 @@
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
import { $props, $prefix, $setup, WithDesignConfigPropsDefineComponent as defineComponent } from '@opentiny/vue-common'
Copy link
Member

Choose a reason for hiding this comment

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

如果这样修改可能会对现有业务造成影响,你这操作相当于包了一层组件转发下props,能不能通过某一个全局配置可以让用户自己选择是否开启这个功能,不要对存量业务造成影响

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.

✨ [Feature]: 组件增加HOC,实现支持在design Config中全局配置默认props
2 participants