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

feat(grid-select): [grid-select] add filter/config features and optimize demo/api docs #2521

Merged
merged 3 commits into from
Nov 14, 2024

Conversation

kagol
Copy link
Member

@kagol kagol commented Nov 14, 2024

PR

主要更新:

  • 增加 filter 可过滤功能,并提供对应的 demo
  • 增加 radio-config/select-config 功能,并提供对应的 demo

效果如下:

可过滤:
image

配置项(禁用和行选择):
image

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

@kagol kagol added documentation Improvements or additions to documentation enhancement New feature or request labels Nov 14, 2024
Copy link

coderabbitai bot commented Nov 14, 2024

Walkthrough

The pull request introduces significant enhancements to the grid-select component's API by adding new properties that improve selection and filtering capabilities. It includes the creation of new Vue components that demonstrate these features, such as single and multi-selection modes, filterable grids, and configuration options. Additionally, new methods for building configurations and filtering data are introduced, along with updates to existing components to accommodate these changes.

Changes

File Path Change Summary
examples/sites/demos/apis/grid-select.js Added new properties (clearable, filterable, filter-method, radio-config, remote, remote-method, reserve-keyword, select-config) to grid-select. Modified IGridOption interface. Introduced IRadioConfig and ISelectConfig interfaces.
examples/sites/demos/pc/app/grid-select/config-composition-api.vue Added new Vue component showcasing single and multiple selection modes using tiny-grid-select. Introduced reactive variables and configuration objects.
examples/sites/demos/pc/app/grid-select/config.vue Added new Vue component implementing grid selection with single and multi-selection scenarios. Defined data and column configurations.
examples/sites/demos/pc/app/grid-select/filter-composition-api.vue Introduced a new Vue component for a filterable grid using TinyGridSelect. Implemented a custom filter method.
examples/sites/demos/pc/app/grid-select/filter.vue Added a new Vue component for grid selection with a filterable interface using TinyGridSelect.
examples/sites/demos/pc/app/grid-select/multiple.vue Updated value property from string to array type for multi-select capabilities in tiny-grid-select.
examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js Added new demo entries for filterable and configuration examples in the grid-select documentation.
packages/renderless/src/grid-select/index.ts Introduced new functions: buildSelectConfig, buildRadioConfig, and filter.
packages/renderless/src/grid-select/vue.ts Updated to include new imports and modified the renderless function to improve data handling and introduce new configurations.
packages/vue/src/grid-select/src/pc.vue Added several new props (filterable, filter-method, select-config, etc.) to enhance the GridSelect component's functionality.

Possibly related PRs

Suggested reviewers

  • zzcr

Poem

🐇 In the grid where choices bloom,
A rabbit hops, dispelling gloom.
With filters set and options clear,
Selecting cities brings us cheer!
So let us play, both single and more,
In this grid, we joyfully explore! 🌼


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 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 removed the documentation Improvements or additions to documentation label Nov 14, 2024
@kagol kagol changed the title feat(grid-select): add filter feature and optimize filter/config docs feat(grid-select): [grid-select] add filter feature and optimize filter/config docs Nov 14, 2024
@kagol kagol added the documentation Improvements or additions to documentation label Nov 14, 2024
@github-actions github-actions bot removed the documentation Improvements or additions to documentation label Nov 14, 2024
@kagol kagol changed the title feat(grid-select): [grid-select] add filter feature and optimize filter/config docs feat(grid-select): [grid-select] add filter/config features and optimize demo/api docs Nov 14, 2024
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: 8

🧹 Outside diff range and nitpick comments (22)
examples/sites/demos/pc/app/grid-select/multiple.vue (3)

20-20: Consider adding type definition for the value property

The change to array type is correct for multiple selection mode. However, consider adding type safety:

-      value: [],
+      value: [] as string[],

Line range hint 40-44: Consider using responsive width

The fixed width of 280px might not be optimal for all screen sizes or content lengths. Consider using relative units or min/max width constraints.

 .tiny-grid-select {
-  width: 280px;
+  min-width: 280px;
+  max-width: 100%;
 }

Property name mismatch and missing filter functionality

The code inspection reveals:

  1. The template uses gridOpMulti but the data property is actually named treeOp
  2. The filter functionality is available through filterable and filter-method props as shown in other examples, but not utilized in this multiple selection demo

Suggested fixes:

  • Rename the prop from :grid-op="gridOpMulti" to :grid-op="treeOp" to match the defined data property
  • If filtering is needed, add the filter functionality:
     <tiny-grid-select
       v-model="value"
       multiple
       :grid-op="treeOp"
       value-field="id"
       text-field="city"
  • filterable
  • :filter-method="filter"



<details>
<summary>🔗 Analysis chain</summary>

Line range hint `1-8`: **Fix undefined property reference and consider adding filter functionality**

1. The template references `gridOpMulti` but this property is not defined in the component.
2. The PR objectives mention adding filter feature, but the template doesn't utilize any filter-related properties.



Let's verify the available filter-related props:



Suggested fix for the undefined property:

```diff
<template>
 <tiny-grid-select
   v-model="value"
   multiple
-    :grid-op="gridOpMulti"
+    :grid-op="treeOp"
   value-field="id"
   text-field="city"
 ></tiny-grid-select>
</template>
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for filter-related props in the grid-select component definition
ast-grep --pattern 'props: {
  $$$
  filter$$$: {
    $$$
  }
  $$$
}'

Length of output: 477


Script:

#!/bin/bash
# Let's check if gridOpMulti is defined in the script section of multiple.vue
cat examples/sites/demos/pc/app/grid-select/multiple.vue

# Also check for filter-related props in the component's source
rg -A 5 "filter" examples/sites/demos/pc/app/grid-select/

Length of output: 4698

examples/sites/demos/pc/app/grid-select/filter.vue (3)

30-35: Enhance column configuration for better accessibility and responsiveness

The current column configuration could be improved in several ways:

  1. Add accessibility attributes to the radio column
  2. Consider more flexible width configurations
  3. Add data type validation
 columns: [
-  { type: 'radio', title: '' },
+  { 
+    type: 'radio', 
+    title: 'Select', 
+    width: 50,
+    headerAttrs: { 'aria-label': 'Selection column' }
+  },
   { field: 'area', title: '区域', width: 90 },
-  { field: 'province', title: '省份', width: 60 },
-  { field: 'city', title: '城市', width: 60 }
+  { field: 'province', title: '省份', minWidth: 80 },
+  { field: 'city', title: '城市', minWidth: 80 }
 ]

43-45: Consider using responsive width

A fixed width of 280px might not be optimal for all screen sizes. Consider using relative units or media queries for better responsiveness.

 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }

1-1: Add component documentation

Since this is a new feature implementation, please add JSDoc comments to document the component's purpose, props, and usage examples.

+/**
+ * @component GridSelectFilter
+ * @description A filterable grid select component that allows users to search and select cities
+ * @example
+ * <tiny-grid-select
+ *   v-model="value"
+ *   filterable
+ *   :filter-method="filter"
+ *   value-field="id"
+ *   text-field="city"
+ *   :grid-op="gridOp"
+ * />
+ */
 <template>
examples/sites/demos/pc/app/grid-select/filter-composition-api.vue (2)

2-9: Consider enhancing component accessibility and error handling.

The grid-select implementation could benefit from:

  1. Adding ARIA attributes for better accessibility
  2. Adding input validation in the filter method
  3. Making value-field and text-field configurable via props for better reusability

Consider applying these enhancements:

 <tiny-grid-select
   v-model="valueSingle"
   filterable
   :filter-method="filter"
   value-field="id"
   text-field="city"
   :grid-op="gridOpSingle"
+  role="combobox"
+  aria-label="City Selection Grid"
 ></tiny-grid-select>

46-48: Consider using responsive width.

The fixed width of 280px might not be optimal for all screen sizes.

Consider using a more responsive approach:

 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }
packages/renderless/src/grid-select/index.ts (2)

1-9: Add TypeScript type annotations for better type safety

Consider adding type annotations to improve maintainability and catch potential runtime errors early:

+interface SelectConfig {
+  checkRowKeys?: any[];
+  [key: string]: any;
+}

-export const buildSelectConfig =
+export const buildSelectConfig = ({
+  props,
+  state
+}: {
+  props: { selectConfig?: SelectConfig };
+  state: { gridCheckedData: any[] };
+}) =>
  () => {
    const checkRowKeys = state.gridCheckedData
    const selectConfig = props.selectConfig || {}

    return Object.assign({}, selectConfig, { checkRowKeys })
  }

10-19: Consider making highlight configurable and add type annotations

The highlight property is hardcoded to true. Consider making it configurable through props for better flexibility.

+interface RadioConfig {
+  checkRowKey?: any;
+  highlight?: boolean;
+  [key: string]: any;
+}

-export const buildRadioConfig =
+export const buildRadioConfig = ({
+  props,
+  state
+}: {
+  props: { radioConfig?: RadioConfig; highlight?: boolean };
+  state: { currentKey: any };
+}) =>
  () => {
    const checkRowKey = state.currentKey
-   const highlight = true
+   const highlight = props.highlight ?? true
    const radioConfig = props.radioConfig || {}

    return Object.assign({}, radioConfig, { checkRowKey, highlight })
  }
examples/sites/demos/pc/app/grid-select/config-composition-api.vue (4)

3-3: Consider internationalizing the scenario descriptions

The hardcoded Chinese text should be replaced with internationalized strings for better maintainability and accessibility.

-    <p>场景1:单选</p>
+    <p>{{ t('gridSelect.singleSelection') }}</p>
-    <p>场景2:多选</p>
+    <p>{{ t('gridSelect.multiSelection') }}</p>

Also applies to: 13-13


5-11: Enhance accessibility and prop organization

The grid-select components could benefit from improved accessibility and consistent prop ordering.

 <tiny-grid-select
+      aria-label="Single selection grid"
       v-model="valueSingle"
+      :grid-op="gridOpSingle"
+      :radio-config="radioConfig"
       value-field="id"
       text-field="city"
-      :grid-op="gridOpSingle"
-      :radio-config="radioConfig"
     ></tiny-grid-select>

Similar changes for the multiple selection grid:

 <tiny-grid-select
+      aria-label="Multiple selection grid"
       v-model="valueMulti"
+      :grid-op="gridOpMulti"
+      :select-config="selectConfig"
       multiple
       value-field="id"
       text-field="city"
-      :grid-op="gridOpMulti"
-      :select-config="selectConfig"
     ></tiny-grid-select>

Also applies to: 15-22


26-31: Consider adding TypeScript type safety

The reactive references could benefit from TypeScript types for better maintainability and IDE support.

-const valueSingle = ref('')
-const valueMulti = ref([])
+const valueSingle = ref<string>('')
+const valueMulti = ref<string[]>([])

80-84: Consider using responsive width

The fixed width might not provide the best user experience across different screen sizes.

 <style scoped>
 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }
 </style>
packages/vue/src/grid-select/src/pc.vue (2)

44-47: Consider narrowing the type of extraQueryParams

The extraQueryParams prop accepts multiple types which could make type checking and validation more difficult. Consider narrowing it to specific types needed for the use case.

-    extraQueryParams: {
-      type: [Object, String, Boolean, Array, Number],
-      default: ''
-    },
+    extraQueryParams: {
+      type: [Object, String],
+      default: ''
+    },

71-83: Document the remote data fetching feature

The addition of remote-related props (remote, remoteConfig, remoteMethod, reserveKeyword) suggests this component now supports remote data fetching. Please ensure this new capability is documented in the component's documentation.

examples/sites/demos/pc/app/grid-select/config.vue (3)

3-3: Consider internationalizing the scenario descriptions

The text "场景1:单选" and "场景2:多选" should be internationalized to support multiple languages.

Consider using a translation system or i18n setup:

-    <p>场景1:单选</p>
+    <p>{{ $t('gridSelect.scenario1') }}</p>
-    <p>场景2:多选</p>
+    <p>{{ $t('gridSelect.scenario2') }}</p>

Also applies to: 13-13


5-11: Enhance accessibility and error handling

The grid-select components could benefit from additional attributes for better accessibility and user experience.

Consider adding these attributes:

     <tiny-grid-select
       v-model="valueSingle"
       value-field="id"
       text-field="city"
       :grid-op="gridOpSingle"
       :radio-config="radioConfig"
+      aria-label="Single selection grid"
+      :loading="isLoading"
+      @error="handleError"
     ></tiny-grid-select>

Also applies to: 15-22


84-88: Consider responsive design

The fixed width of 280px might not be optimal for all screen sizes.

Consider using responsive width:

 <style scoped>
 .tiny-grid-select {
-  width: 280px;
+  width: 100%;
+  max-width: 280px;
+  min-width: 200px;
 }
 </style>
examples/sites/demos/apis/grid-select.js (3)

8-41: Enhance English descriptions for better clarity.

The English descriptions could be more comprehensive and consistent:

Apply these improvements:

-            'en-US': 'Whether to display the one click clear button, only applicable to radio selection'
+            'en-US': 'Whether to display a one-click clear button (only applicable to radio selection mode)'
-            'en-US': 'Is it searchable'
+            'en-US': 'Whether the grid-select supports filtering/searching'
-            'en-US': 'Custom filtering method'
+            'en-US': 'Custom method for filtering grid items. Accepts a query string parameter.'

88-121: Simplify and improve remote search related descriptions.

The descriptions for remote search functionality could be more concise and clear.

Apply these improvements:

-            'en-US': 'Is it a remote search'
+            'en-US': 'Whether to enable remote search functionality'
-            'en-US': 'Remote search methods'
+            'en-US': 'Method to handle remote search queries'
-            'en-US':
-              'When selecting multiple searchable options, do you still keep the current search keywords after selecting one option'
+            'en-US': 'In multi-select mode, preserve search keyword after selecting an option'

171-181: Consider adding inline type definitions.

Instead of just referencing external documentation, consider adding inline type definitions for IRadioConfig and ISelectConfig. This would make the API documentation more self-contained and easier to use.

Example structure:

-同 Grid 组件的 IRadioConfig:https://opentiny.design/tiny-vue/zh-CN/smb-theme/components/grid#api
+interface IRadioConfig {
+  // Add type definitions here
+  checkMethod?: (row: any) => boolean;
+  // ... other properties
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad17d8 and 88b20c9.

📒 Files selected for processing (10)
  • examples/sites/demos/apis/grid-select.js (3 hunks)
  • examples/sites/demos/pc/app/grid-select/config-composition-api.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/config.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/filter-composition-api.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/filter.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/multiple.vue (1 hunks)
  • examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js (1 hunks)
  • packages/renderless/src/grid-select/index.ts (1 hunks)
  • packages/renderless/src/grid-select/vue.ts (1 hunks)
  • packages/vue/src/grid-select/src/pc.vue (2 hunks)
🧰 Additional context used
🪛 Biome
packages/renderless/src/grid-select/vue.ts

[error] 24-24: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (16)
packages/renderless/src/grid-select/vue.ts (3)

1-1: LGTM: Import and API exports are properly structured

The new imports are correctly added and consistently exposed through the API array.

Also applies to: 3-3


14-19: LGTM: API methods are properly configured

The new API methods are correctly implemented with proper dependency injection of props, state, and vm.


22-26: Improve watcher implementation

Several suggestions for the watcher:

  1. The assignment in the callback could be clearer
  2. Consider adding null checks
  3. The deep watch might impact performance for large datasets

Consider this implementation:

   watch(
     () => props.gridOp.data,
-    (data) => data && (state.gridData = data),
+    (data) => {
+      if (data !== undefined) {
+        state.gridData = data
+      }
+    },
-    { immediate: true, deep: true }
+    { immediate: true }
   )

Let's check if the deep watch is necessary by looking for nested data structures in the grid data:

✅ Verification successful

The previous command might have failed because the path pattern was too specific. Let's try a broader search to understand the grid data structure and its usage.


Based on the search results, I can see that the grid data structure is commonly used across many components and often contains nested objects. For example:

  1. In grid-select examples, the data contains nested objects:
data: [
  { id: '001', area: '华南区', province: '广东省', city: '广州市' },
  { id: '002', area: '华南区', province: '广东省', city: '深圳市' },
  ...
]
  1. The grid data is used in various components like popeditor, dialog-select, etc. with nested configurations including columns, data arrays with objects.

Let me verify the specific usage in the grid-select component:


Based on the evidence gathered, I can now provide a final response about the deep watch in the grid-select component:

Deep watch can be safely removed

The deep watch on props.gridOp.data appears unnecessary because:

  1. The data is directly assigned without any transformation or nested property access: state.gridData = data
  2. No methods were found that manipulate nested properties of the grid data
  3. The component acts as a pass-through for the data prop, maintaining a simple 1:1 relationship

The original review comment's suggestion to remove deep watch is correct, as it would improve performance without affecting functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for nested data structures in grid-related files
rg -l "gridOp.*data.*=" | xargs rg -A 5 "data.*=.*\[.*\{" 

Length of output: 59


Script:

#!/bin/bash
# Let's try a different approach to find grid data structure usage
# First, find files containing gridOp
rg -l "gridOp"

# Then search for data structure initialization or type definitions
rg -A 5 "interface.*Grid|type.*Grid|gridData.*=|gridOp.*=" 

Length of output: 66481


Script:

#!/bin/bash
# Search for gridData usage in grid-select component
ast-grep --pattern 'state.gridData = $_' packages/renderless/src/grid-select/

Length of output: 172


Script:

#!/bin/bash
# Let's check how gridData is used in the component
ast-grep --pattern 'state.gridData' packages/renderless/src/grid-select/

# Also check if there are any methods that process the grid data
rg -A 5 "function.*grid.*data" packages/renderless/src/grid-select/

Length of output: 235

🧰 Tools
🪛 Biome

[error] 24-24: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

examples/sites/demos/pc/app/grid-select/multiple.vue (1)

Line range hint 1-44: Verify alignment with PR objectives

The PR objectives mention adding a filter feature, but this example doesn't demonstrate it. Consider:

  1. Adding a filter example to this component
  2. Creating a separate example specifically for the filter feature

Let's check if there are other filter-related examples:

#!/bin/bash
# Search for other filter-related examples in the demos directory
fd "filter" examples/sites/demos/pc/app/grid-select/
examples/sites/demos/pc/app/grid-select/filter.vue (1)

13-18: LGTM! Component registration is properly implemented

The import and component registration follow Vue.js best practices.

examples/sites/demos/pc/app/grid-select/filter-composition-api.vue (1)

13-14: LGTM!

The imports are clean and well-organized, importing only the necessary functions and components.

examples/sites/demos/pc/app/grid-select/webdoc/grid-select.js (2)

47-59: Verify configuration options in implementation and API docs.

The documentation for the config demo is well-structured with complete bilingual support. However, let's ensure consistency across the codebase.

Let's verify the implementation and API documentation:

#!/bin/bash
# Description: Verify the config demo implementation and API documentation
# Expected: 
# 1. config.vue should exist and contain select-config/radio-config implementations
# 2. API docs should document these configuration options

# Check for the demo implementation
fd -t f "config.vue" -x grep -l "select-config|radio-config|checkMethod" {} \;

# Check API documentation for these properties
fd -t f "grid-select.js" -p "apis" -x grep -l "select-config|radio-config|checkMethod" {} \;

33-46: Add missing English description for the filter demo.

The English description ('en-US') is empty, which makes the documentation incomplete for international users. Please add an appropriate English description matching the Chinese content about the filterable and filter-method properties.

Let's verify the referenced Vue file exists:

packages/renderless/src/grid-select/index.ts (1)

20-35: ⚠️ Potential issue

Critical: Improve error handling and avoid direct ref manipulation

The current implementation has several issues that need to be addressed:

  1. Direct manipulation of $refs is an anti-pattern in Vue and makes testing difficult
  2. Missing error handling for undefined refs
  3. No validation of filterMethod
  4. Forced scrolling might not be desired in all cases

Consider refactoring to:

 export const filter =
   ({ props, state, vm }) =>
   (value) => {
+    if (typeof props.filterMethod !== 'function') {
+      console.warn('filterMethod must be a function')
+      return
+    }
+
     const { filterMethod } = props
 
-    const table = vm.$refs.gridRef.$refs.tinyTable
+    const gridRef = vm.$refs.gridRef
+    if (!gridRef) {
+      console.warn('Grid reference not found')
+      return
+    }
+
+    const table = gridRef.$refs.tinyTable
+    if (!table) {
+      console.warn('Table reference not found')
+      return
+    }
+
     const fullData = table.afterFullData
 
-    vm.$refs.gridRef.scrollTo(null, 0)
+    // Make scrolling behavior configurable
+    if (props.autoScrollOnFilter) {
+      gridRef.scrollTo(null, 0)
+    }
 
-    table.afterFullData = filterMethod(value, fullData) || []
+    const filteredData = filterMethod(value, fullData) || []
+    table.afterFullData = filteredData
 
-    vm.$refs.gridRef.handleTableData(!value)
+    gridRef.handleTableData(!value)
 
     state.previousQuery = value
   }

Let's verify the usage of filterMethod in the codebase:

examples/sites/demos/pc/app/grid-select/config-composition-api.vue (1)

28-28: Verify TinyGridSelect component registration

Let's verify that the TinyGridSelect component is properly registered and available throughout the application.

✅ Verification successful

TinyGridSelect component integration is correctly implemented

The component is properly imported and used in the file:

  • Import statement is correct: import { TinyGridSelect } from '@opentiny/vue'
  • Component is used correctly in template with <tiny-grid-select> tags
  • No component registration is needed in the Composition API setup as the import automatically makes the component available in the template
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if TinyGridSelect is consistently imported and registered across the application

# Search for TinyGridSelect imports
echo "Checking TinyGridSelect imports:"
rg "import.*TinyGridSelect.*from '@opentiny/vue'" -A 2

# Search for global component registration
echo "\nChecking global component registration:"
rg "app\.component.*tiny-grid-select" -i

Length of output: 2318


Script:

#!/bin/bash
# Let's check the template usage of TinyGridSelect in the file
echo "Checking TinyGridSelect template usage in config-composition-api.vue:"
rg -A 5 "<tiny-grid-select" "examples/sites/demos/pc/app/grid-select/config-composition-api.vue"

# Let's also check if there's any local component registration
echo "\nChecking local component registration:"
ast-grep --pattern 'components: {
  $$$
}'

Length of output: 685

packages/vue/src/grid-select/src/pc.vue (3)

7-8: LGTM: Filter feature implementation looks good

The addition of filterable and filter-method props aligns well with the PR objectives for adding filtering capabilities.


61-70: Review the default checkMethod implementation

Both radioConfig and selectConfig have a default checkMethod that always returns true. This might be too permissive as it allows all selections by default. Consider:

  1. Adding validation logic in the default implementation
  2. Documenting the expected use case for checkMethod
#!/bin/bash
# Search for existing checkMethod implementations for reference
ast-grep --pattern 'checkMethod() {
  $$$
}'

Also applies to: 84-93


15-16: Verify the impact of configuration and binding changes

Several significant changes have been made:

  1. New configuration builders for select and radio
  2. Changed v-bind from state.gridData to gridOp

Let's verify the changes don't break existing functionality.

Also applies to: 24-24

✅ Verification successful

Configuration changes appear to be safe and consistent

The changes are verified to be correct and safe because:

  1. The buildSelectConfig() and buildRadioConfig() methods are used to configure the grid component while maintaining the existing props interface
  2. The gridOp binding is correctly used alongside state.gridData - they serve different purposes:
    • state.gridData provides columns and data
    • gridOp provides additional grid options as defined in props

The changes maintain backward compatibility as both the old data bindings and new configurations coexist properly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing usages of state.gridData in grid-select components
rg -t vue 'state\.gridData' --glob 'packages/vue/src/grid-select/**'

# Search for gridOp usage to understand the expected properties
rg -t vue 'gridOp' --glob 'packages/vue/src/grid-select/**'

Length of output: 140


Script:

#!/bin/bash
# Let's try a more comprehensive search for the configuration methods and state usage
rg -t vue "buildSelectConfig|buildRadioConfig|state\.gridData|gridOp" packages/vue/src/grid-select/

# Also search for any select or radio config related code
rg -t vue "selectConfig|radioConfig" packages/vue/src/grid-select/

Length of output: 226


Script:

#!/bin/bash
# Search without file type restriction since vue might not be recognized
rg "buildSelectConfig|buildRadioConfig|state\.gridData|gridOp" packages/vue/src/grid-select/

# Look for the actual file content to understand the context better
cat packages/vue/src/grid-select/src/pc.vue

Length of output: 2912

examples/sites/demos/pc/app/grid-select/config.vue (1)

5-22: Verify API compatibility

Let's ensure the component properly implements all required grid-select API features.

✅ Verification successful

Let me gather more specific information about the API implementation and usage patterns.


Based on the implementation and examples I've seen, I can now provide the final response:

Grid-select implementation is correctly following the component's API

The code under review correctly implements the grid-select component with proper props:

  • Uses required props: v-model, value-field, text-field, grid-op
  • Correctly implements both single and multiple selection scenarios
  • Properly configures radio/selection behavior with radioConfig and selectConfig
  • Matches the implementation patterns shown in other examples

The component usage is consistent with both the main implementation in pc.vue and other example files, demonstrating proper integration with the grid-select API.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistency with API definition and other examples

# Search for grid-select API definition
echo "Searching for API definition..."
rg -l "grid-select.*api" --type vue

# Find other grid-select examples for comparison
echo "Finding other examples..."
fd -e vue -p "grid-select.*\.vue$"

# Check for consistent prop usage
echo "Checking prop usage..."
ast-grep --pattern 'tiny-grid-select $props={$_}'

Length of output: 944


Script:

#!/bin/bash
# Check the implementation and usage of grid-select props

# Examine the main component implementation
echo "=== Main Component Implementation ==="
cat packages/vue/src/grid-select/src/pc.vue

# Look at other example implementations for comparison
echo -e "\n=== Basic Usage Example ==="
cat examples/sites/demos/pc/app/grid-select/basic-usage.vue

echo -e "\n=== Multiple Selection Example ==="
cat examples/sites/demos/pc/app/grid-select/multiple.vue

# Search for prop definitions and validations
echo -e "\n=== Searching for prop definitions ==="
rg "props:\s*{" -A 10 packages/vue/src/grid-select/src/pc.vue

Length of output: 5022

examples/sites/demos/apis/grid-select.js (2)

165-166: Good type safety improvement!

The change from Record<string, any> to Record<string, unknown> improves type safety by being more explicit about unknown types.


76-87: Verify external Grid component documentation links.

Both radio-config and select-config reference the Grid component's documentation. Let's ensure these references are valid and accessible.

Also applies to: 122-133

✅ Verification successful

Let me try a different approach to find these configuration types in the codebase. The previous command failed because Vue file type wasn't recognized, but the types are likely defined in TypeScript/JavaScript files.


I found that the types are referenced in grid.js. Let's examine its content to verify the type definitions and their structure.


I found the types are referenced in the API documentation but let's verify their actual interface definitions in the codebase.


External type references are valid and documented

The radio-config and select-config props reference IRadioConfig and ISelectConfig types which are properly documented in the API documentation. The types are defined with clear interfaces:

  • IRadioConfig includes configuration for radio button behavior like trigger mode, label field, etc.
  • ISelectConfig includes configuration for checkbox/selection behavior like trigger mode, label field, etc.

The documentation links are valid as they reference internal type definitions rather than external documentation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Grid component documentation links

# Check if the Grid component documentation exists
rg -l "IRadioConfig|ISelectConfig" --type vue

Length of output: 75


Script:

#!/bin/bash
# Search for type definitions in TypeScript/JavaScript files
rg -l "interface IRadioConfig|interface ISelectConfig|type IRadioConfig|type ISelectConfig"

# Search for imports of these types
rg "import.*IRadioConfig|import.*ISelectConfig"

Length of output: 177


Script:

#!/bin/bash
# Let's examine the content of grid.js to see the type definitions
cat examples/sites/demos/apis/grid.js

Length of output: 150194


Script:

#!/bin/bash
# Let's check if IRadioConfig and ISelectConfig are used in the codebase
ast-grep --pattern 'interface IRadioConfig'
ast-grep --pattern 'interface ISelectConfig'

Length of output: 92

Comment on lines 9 to +10
value: props.modelValue,
gridData: props.gridOp
gridData: props.gridOp.data
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding validation for gridOp data

The direct assignment of props.gridOp.data to state might be unsafe if gridOp is undefined.

Consider adding validation:

-    gridData: props.gridOp.data
+    gridData: props.gridOp?.data || []
📝 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
value: props.modelValue,
gridData: props.gridOp
gridData: props.gridOp.data
value: props.modelValue,
gridData: props.gridOp?.data || []

Comment on lines +2 to +9
<tiny-grid-select
v-model="valueSingle"
filterable
:filter-method="filter"
value-field="id"
text-field="city"
:grid-op="gridOpSingle"
></tiny-grid-select>
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

Implement the missing filter method and enhance accessibility

The template references a filter-method but the implementation is missing. Additionally, consider adding accessibility attributes.

Add the missing filter method to the component:

 export default {
   // ... existing code ...
+  methods: {
+    filter(value, queryString) {
+      return value.city.toLowerCase().includes(queryString.toLowerCase())
+    }
+  }
 }

Enhance accessibility:

 <tiny-grid-select
   v-model="valueSingle"
   filterable
   :filter-method="filter"
   value-field="id"
   text-field="city"
   :grid-op="gridOpSingle"
+  aria-label="City Selection Grid"
+  role="grid"
 ></tiny-grid-select>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +16 to +32
const valueSingle = ref('')

const gridOpSingle = reactive({
data: [
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
],
columns: [
{ type: 'radio', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving data management and accessibility.

The current implementation could be enhanced by:

  1. Moving static data to a separate constant or service
  2. Making column widths responsive
  3. Adding an accessible label for the radio column

Consider refactoring like this:

-const gridOpSingle = reactive({
+// Move to separate file: constants/cityData.js
+const CITY_DATA = [
+  { id: '001', area: '华南区', province: '广东省', city: '广州市' },
+  // ... other cities
+]
+
+const gridOpSingle = reactive({
-  data: [
-    { id: '001', area: '华南区', province: '广东省', city: '广州市' },
-    // ... other entries
-  ],
+  data: CITY_DATA,
   columns: [
-    { type: 'radio', title: '' },
+    { type: 'radio', title: '', ariaLabel: 'Select City' },
-    { field: 'area', title: '区域', width: 90 },
+    { field: 'area', title: '区域', minWidth: 90 },
     // ... other columns with minWidth
   ]
 })

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +34 to +42
const filter = (value) => {
if (!value) {
return gridOpSingle.data
}

return gridOpSingle.data.filter((item) => {
return item.city.includes(value)
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance filter implementation for better search functionality.

The current filter implementation could be improved for better user experience and performance:

  1. Add case-insensitive search
  2. Trim input values
  3. Support partial matches
  4. Consider debouncing for performance with large datasets

Consider this enhanced implementation:

-const filter = (value) => {
+const filter = (value) => {
+  const searchValue = value?.trim().toLowerCase()
+
-  if (!value) {
+  if (!searchValue) {
     return gridOpSingle.data
   }
 
   return gridOpSingle.data.filter((item) => {
-    return item.city.includes(value)
+    return item.city.toLowerCase().includes(searchValue) ||
+           item.area.toLowerCase().includes(searchValue) ||
+           item.province.toLowerCase().includes(searchValue)
   })
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +65 to +77
const radioConfig = ref({
trigger: 'row',
checkMethod({ rowIndex }) {
return rowIndex % 2 === 1
}
})

const selectConfig = ref({
trigger: 'row',
checkMethod({ rowIndex }) {
return rowIndex % 2 === 0
}
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize selection configuration and improve error handling

The selection configurations can be improved for better maintainability and robustness.

+const isRowSelectable = (isEven) => ({ rowIndex }) => rowIndex % 2 === isEven

 const radioConfig = ref({
   trigger: 'row',
-  checkMethod({ rowIndex }) {
-    return rowIndex % 2 === 1
-  }
+  checkMethod: isRowSelectable(false)
 })

 const selectConfig = ref({
   trigger: 'row',
-  checkMethod({ rowIndex }) {
-    return rowIndex % 2 === 0
-  }
+  checkMethod: isRowSelectable(true)
 })

+// Add error handling for invalid row indices
+const validateRowIndex = ({ rowIndex }) => {
+  if (rowIndex < 0) {
+    console.warn('Invalid row index:', rowIndex)
+    return false
+  }
+  return true
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +33 to +63
const gridOpSingle = reactive({
data: [
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
],
columns: [
{ type: 'radio', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})

const gridOpMulti = reactive({
data: [
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
],
columns: [
{ type: 'selection', title: '' },
{ field: 'area', title: '区域', width: 90 },
{ field: 'province', title: '省份', width: 60 },
{ field: 'city', title: '城市', width: 60 }
]
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor duplicate data and improve column configuration

The grid configurations contain duplicate data and could benefit from several improvements:

  1. Extract shared data to avoid duplication:
+const gridData = [
+  { id: '001', area: '华南区', province: '广东省', city: '广州市' },
+  // ... other data
+]

 const gridOpSingle = reactive({
-  data: [
-    { id: '001', area: '华南区', province: '广东省', city: '广州市' },
-    // ... duplicate data
-  ],
+  data: gridData,
   columns: [/*...*/]
 })

 const gridOpMulti = reactive({
-  data: [
-    { id: '001', area: '华南区', province: '广东省', city: '广州市' },
-    // ... duplicate data
-  ],
+  data: gridData,
   columns: [/*...*/]
 })
  1. Consider using relative widths for better responsiveness:
 columns: [
   { type: 'radio', title: '' },
-  { field: 'area', title: '区域', width: 90 },
+  { field: 'area', title: '区域', width: '30%' },
   // ... other columns
 ]
  1. Add JSDoc comments to document the column configuration:
+/**
+ * @typedef {Object} GridColumn
+ * @property {string} field - The data field to display
+ * @property {string} title - The column header text
+ * @property {string|number} width - The column width
+ * @property {string} [type] - Special column type (radio|selection)
+ */

 const gridOpSingle = reactive({
   data: gridData,
+  /** @type {GridColumn[]} */
   columns: [/*...*/]
 })

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +69 to +71
checkMethod({ rowIndex }) {
return rowIndex % 2 === 1
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve selection logic maintainability

The current row selection logic using modulo operations (rowIndex % 2) is not intuitive and might be confusing for maintenance.

Consider using a more explicit approach:

       checkMethod({ rowIndex }) {
-         return rowIndex % 2 === 1
+         return this.isRowSelectable(rowIndex)
       }

+      isRowSelectable(rowIndex) {
+        // Clear documentation of selection logic
+        const isOddRow = rowIndex % 2 === 1
+        return isOddRow
+      }

Also applies to: 75-77

Comment on lines +38 to +44
data: [
{ id: '001', area: '华南区', province: '广东省', city: '广州市' },
{ id: '002', area: '华南区', province: '广东省', city: '深圳市' },
{ id: '003', area: '华南区', province: '广东省', city: '珠海市' },
{ id: '004', area: '华南区', province: '广东省', city: '佛山市' },
{ id: '005', area: '华南区', province: '广东省', city: '中山市' }
],
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Externalize grid data

The grid data is currently hardcoded in the component. This makes it difficult to maintain and update.

Consider moving the data to a separate file:

+// gridData.js
+export const citiesData = [
+  { id: '001', area: '华南区', province: '广东省', city: '广州市' },
+  // ... other cities
+]

-      gridOpSingle: {
-        data: [
-          { id: '001', area: '华南区', province: '广东省', city: '广州市' },
-          // ... other entries
-        ],
+      gridOpSingle: {
+        data: citiesData,

Also applies to: 53-59

@zzcr zzcr merged commit 554e778 into dev Nov 14, 2024
22 of 30 checks passed
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