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] Marker 컴포넌트, 스토리북 #19

Merged
merged 7 commits into from
Oct 23, 2024
Merged

[FEAT] Marker 컴포넌트, 스토리북 #19

merged 7 commits into from
Oct 23, 2024

Conversation

keemsebin
Copy link
Member

@keemsebin keemsebin commented Oct 22, 2024

관련 이슈

close #13

📑 작업 내용

  • Marker 컴포넌트, 스토리북 구현했습니다.
image

💬 리뷰 중점 사항/기타 참고 사항

Summary by CodeRabbit

  • New Features

    • Introduced a new Marker icon for enhanced visual representation in the application.
    • Added a Marker component that allows users to select various categories (e.g., restaurant, cafe, bar) through a button interface.
    • Created Storybook stories for the Marker component to visualize its usage in different scenarios.
    • Added a constant MARKER_CATEGORIES for predefined category options.
  • Bug Fixes

    • None
  • Documentation

    • Updated type definitions to include a new Props type for the Marker component.
  • Chores

    • Organized and structured the Marker component for better maintainability and usability.

@keemsebin keemsebin added the feat 기능 label Oct 22, 2024
@keemsebin keemsebin self-assigned this Oct 22, 2024
@keemsebin keemsebin requested a review from houony as a code owner October 22, 2024 17:42
Copy link

coderabbitai bot commented Oct 22, 2024

Walkthrough

The pull request introduces a new Marker component along with its associated assets and Storybook stories. The Marker component is designed to render a button with an icon, accepting a categoryName prop that allows for predefined categories. Additionally, the Marker SVG icon is imported and included in the Icons object. The Storybook stories provide a structured way to visualize the Marker component in various scenarios, enhancing the development and testing process.

Changes

File Change Summary
src/assets/icons/index.tsx Added import for Marker SVG and included it in the Icons object.
src/components/common/Marker/Marker.stories.tsx Introduced Storybook stories for the Marker component, including Basic and ALL variations.
src/components/common/Marker/Marker.types.ts Added new type Props for the Marker component, specifying categoryName and extending button props.
src/components/common/Marker/index.tsx Created Marker component that renders a button with an icon and accepts props of type Props.
src/constants/categories.ts Added constant MARKER_CATEGORIES and type MarkerCategory for category management.

Assessment against linked issues

Objective Addressed Explanation
Implement the Marker component and Storybook
Create Storybook stories for the Marker component

🐰 In the garden where markers bloom,
A button shines, dispelling gloom.
With stories told in vibrant hues,
Choose your path, the world to peruse!
Icons dance, with joy they play,
In the land of markers, come what may! 🌼✨


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.

Copy link

github-actions bot commented Oct 22, 2024

Update: 2024년 10월 23일 04시 16분 15초
Storybook 미리보기: https://6709628f8a6eec04b29d4f39-hovdfxzswf.chromatic.com/

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

🧹 Outside diff range and nitpick comments (3)
src/components/common/Marker/index.tsx (1)

9-16: Consider adding error boundaries or fallback UI

The component might fail silently if an invalid categoryName is provided or if the Icon component fails to load.

Consider implementing:

  1. PropTypes or TypeScript union type for valid category names
  2. Error boundary for catching Icon component failures
  3. Fallback UI when icon loading fails

Would you like me to help implement these safety measures?

src/components/common/Marker/Marker.stories.tsx (2)

1-13: Document the reason for ESLint disable comment

Consider adding a brief comment explaining why the no-restricted-exports rule needs to be disabled here. This helps future maintainers understand if this is a Storybook requirement or a specific project need.

-/* eslint-disable no-restricted-exports */
+/* eslint-disable no-restricted-exports -- Required for Storybook default export pattern */

19-29: Simplify the Basic story render function

The render function can be simplified since it's just spreading args without any additional logic.

 export const Basic: Story = {
   args: {
     categoryName: 'restaurant',
   },
   argTypes: {
     categoryName: { control: { type: 'select' }, options: categoryNames },
   },
-  render: (args) => {
-    return <Marker {...args} />;
-  },
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1069a30 and 3d2e9a0.

⛔ Files ignored due to path filters (1)
  • src/assets/icons/marker.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • src/assets/icons/index.tsx (2 hunks)
  • src/components/common/Marker/Marker.stories.tsx (1 hunks)
  • src/components/common/Marker/Marker.types.ts (1 hunks)
  • src/components/common/Marker/index.tsx (1 hunks)
🧰 Additional context used
🔇 Additional comments (5)
src/components/common/Marker/Marker.types.ts (1)

1-16: LGTM! Clean type definition with good practices.

The type definition is well-structured, using proper TypeScript practices:

  • Correctly extends button props using ComponentPropsWithoutRef
  • Uses union type for strict category names
  • Includes JSDoc documentation
src/components/common/Marker/index.tsx (2)

12-12: Validate categoryName prop

The component doesn't validate if the provided categoryName is valid for the Icon component.

Let's check what icon names are supported:

#!/bin/bash
# Search for Icon component implementation to see supported names
rg -t typescript "export type IconName" src/components/common/Icon/

1-6: Verify Props type definition

The Props type is imported but not visible in the current context. Please ensure it properly defines the categoryName prop and documents any additional props that can be spread onto the button element.

✅ Verification successful

Let me try a different approach to locate the Props type definition.

These commands will help us:

  1. Locate the types file specifically
  2. View its contents if found
  3. Find any type definitions in the Marker directory
  4. Check for the types file with different extensions

This will give us a comprehensive view of where and how the Props type is defined.


Props type definition is well-defined and properly documented

The Props type is correctly defined in Marker.types.ts with:

  • A properly documented categoryName prop with specific string literal types
  • Extends ComponentPropsWithoutRef<'button'> to allow spreading button props
  • Clear JSDoc documentation for the categoryName prop
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Props type definition
rg -t typescript "type Props" src/components/common/Marker/

Length of output: 104


Script:

#!/bin/bash
# Search for Props type definition using fd to find the types file first
fd "Marker.types.ts" src/

# If the file exists, let's see its content
fd "Marker.types.ts" src/ --exec cat {}

Length of output: 412


Script:

#!/bin/bash
# As a fallback, let's also check for any typescript interface or type definitions in Marker directory
rg "interface|type" src/components/common/Marker/

Length of output: 591


Script:

#!/bin/bash
# Let's also verify if the file extension might be different
fd "Marker.types" src/

Length of output: 67

src/assets/icons/index.tsx (2)

19-19: LGTM! Import statement follows conventions.

The Marker SVG import follows the established naming and import conventions used for other icons in the file.


42-42: LGTM! Icons object entry is consistent.

The marker icon is properly added to the Icons object following the camelCase naming convention used for other entries.

Let's verify the Marker SVG file exists:

✅ Verification successful

Let me try a different approach to verify the existence of the marker SVG file.

This will help us verify both the existence of the marker.svg file and understand the icons directory structure.


The marker icon file exists and is properly placed in the icons directory

The verification confirms that marker.svg exists at src/assets/icons/marker.svg alongside other SVG icons that are part of the Icons object. The file structure and naming convention are consistent with the codebase organization.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the marker.svg file exists in the icons directory
fd marker.svg --type f --parent-dir src/assets/icons

Length of output: 323


Script:

#!/bin/bash
# Search for marker.svg file in the repository
fd "marker.svg"

# Also let's check the content of the icons directory to understand the structure
fd . "src/assets/icons" --type f

Length of output: 843

Comment on lines 3 to 15
export type Props = {
/**
* category name to be displayed.
*/
categoryName:
| 'restaurant'
| 'cafe'
| 'bar'
| 'shopping'
| 'travel'
| 'public'
| 'hospital'
| 'other';
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 enhancing type safety and reusability.

A few suggestions to improve the type definition:

  1. Extract categories to a separate constant/enum for reusability
  2. Enhance the JSDoc documentation with example usage
  3. Consider adding a default category

Here's how you could improve it:

 import { ComponentPropsWithoutRef } from 'react';

+export const MARKER_CATEGORIES = {
+  RESTAURANT: 'restaurant',
+  CAFE: 'cafe',
+  BAR: 'bar',
+  SHOPPING: 'shopping',
+  TRAVEL: 'travel',
+  PUBLIC: 'public',
+  HOSPITAL: 'hospital',
+  OTHER: 'other',
+} as const;
+
+export type MarkerCategory = typeof MARKER_CATEGORIES[keyof typeof MARKER_CATEGORIES];
+
 export type Props = {
   /**
    * category name to be displayed.
+   * @example
+   * ```tsx
+   * <Marker categoryName="restaurant" />
+   * ```
    */
-  categoryName:
-    | 'restaurant'
-    | 'cafe'
-    | 'bar'
-    | 'shopping'
-    | 'travel'
-    | 'public'
-    | 'hospital'
-    | 'other';
+  categoryName: MarkerCategory;
 } & ComponentPropsWithoutRef<'button'>;

This approach:

  • Makes categories reusable across the application
  • Provides better type safety with const assertions
  • Improves documentation with usage examples
  • Makes it easier to maintain categories in one place

Comment on lines 7 to 8
export const Marker = ({ categoryName, ...props }: Props) => {
return (
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add accessibility attributes to the button

The button lacks accessibility attributes which are crucial for screen readers.

Add an appropriate aria-label:

-<button className="relative inline-block" {...props}>
+<button 
+  aria-label={`${categoryName} location marker`}
+  className="relative inline-block" 
+  {...props}
+>
📝 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
export const Marker = ({ categoryName, ...props }: Props) => {
return (
export const Marker = ({ categoryName, ...props }: Props) => {
return (
<button
aria-label={`${categoryName} location marker`}
className="relative inline-block"
{...props}
>

Comment on lines 11 to 15
<Icon
name={categoryName}
size={35}
className={`absolute left-[51%] ${categoryName == 'restaurant' ? 'top-[55%]' : 'top-1/2'} -translate-x-1/2 -translate-y-1/2`}
/>
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 extracting magic numbers and improving maintainability

The component has several hard-coded values that could be made more maintainable:

  • Icon size of 35
  • Left position of 51%
  • Top position of 55%

Consider creating constants or theme variables:

+const ICON_SIZE = 35;
+const ICON_POSITIONS = {
+  default: { left: '51%', top: '50%' },
+  restaurant: { left: '51%', top: '55%' }
+} as const;
+
 export const Marker = ({ categoryName, ...props }: Props) => {
+  const position = categoryName === 'restaurant' 
+    ? ICON_POSITIONS.restaurant 
+    : ICON_POSITIONS.default;
+
   return (
     <button className="relative inline-block" {...props}>
       <MarkerIcon />
       <Icon
         name={categoryName}
-        size={35}
-        className={`absolute left-[51%] ${categoryName == 'restaurant' ? 'top-[55%]' : 'top-1/2'} -translate-x-1/2 -translate-y-1/2`}
+        size={ICON_SIZE}
+        className={`absolute left-[${position.left}] top-[${position.top}] -translate-x-1/2 -translate-y-1/2`}
       />

Committable suggestion was skipped due to low confidence.

Comment on lines 31 to 41
export const ALL: Story = {
render: () => {
return (
<>
{categoryNames.map((item) => (
<Marker key={item} categoryName={item} />
))}
</>
);
},
};
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 the ALL story with description and layout

Add a description for the story and improve the layout to better showcase all variants.

 export const ALL: Story = {
+  parameters: {
+    docs: {
+      description: {
+        story: 'Displays all available marker categories in a grid layout.',
+      },
+    },
+  },
   render: () => {
     return (
-      <>
+      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '1rem' }}>
         {categoryNames.map((item) => (
           <Marker key={item} categoryName={item} />
         ))}
-      </>
+      </div>
     );
   },
 };
📝 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
export const ALL: Story = {
render: () => {
return (
<>
{categoryNames.map((item) => (
<Marker key={item} categoryName={item} />
))}
</>
);
},
};
export const ALL: Story = {
parameters: {
docs: {
description: {
story: 'Displays all available marker categories in a grid layout.',
},
},
},
render: () => {
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '1rem' }}>
{categoryNames.map((item) => (
<Marker key={item} categoryName={item} />
))}
</div>
);
},
};

Comment on lines 15 to 17
const categoryNames: Array<
'restaurant' | 'cafe' | 'bar' | 'shopping' | 'travel' | 'public' | 'hospital' | 'other'
> = ['restaurant', 'cafe', 'bar', 'shopping', 'travel', 'public', 'hospital', 'other'];
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Now I have enough information to provide the final response:

The review suggestion is valid and important

The category names are currently defined in two places:

  • As a union type in src/components/common/Marker/Marker.types.ts
  • As an array in src/components/common/Marker/Marker.stories.tsx

Moving these categories to a shared constants file would be beneficial because:

  1. It would eliminate the duplication between the stories and types files
  2. The shared constant could be used to derive both the TypeScript type and the array of values
  3. Any future category changes would only need to be updated in one place

Example structure for the proposed constants file:

// src/constants/categories.ts
export const MARKER_CATEGORIES = ['restaurant', 'cafe', 'bar', 'shopping', 'travel', 'public', 'hospital', 'other'] as const;
export type MarkerCategory = typeof MARKER_CATEGORIES[number];
🔗 Analysis chain

Consider moving category names to a shared constants file

The category names array should be moved to a shared constants file (e.g., src/constants/categories.ts) since these values are likely needed by both the Marker component and its stories. This ensures consistency and makes maintenance easier.

Let's check if these categories are defined elsewhere:


Let me search for any usage of these category names individually to ensure we're not missing any other definitions or usages.


Let me check the content of Marker.types.ts to see how these categories are defined there, as it seems to be the main type definition file.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for category name definitions across the codebase
rg -l "restaurant.*cafe.*bar.*shopping.*travel.*public.*hospital.*other"

Length of output: 122


Script:

#!/bin/bash
# Search for individual category names and their usage in type definitions or constants
rg -A 2 -B 2 "type.*Category.*=.*(" || true
rg -A 2 -B 2 "interface.*Category.*{" || true
rg -A 2 -B 2 "enum.*Category.*{" || true

# Search for individual category names in quotes
echo "Searching for individual categories:"
for category in restaurant cafe bar shopping travel public hospital other; do
    echo -e "\nFiles containing '$category':"
    rg -l "'$category'" || true
    rg -l "\"$category\"" || true
done

Length of output: 2926


Script:

#!/bin/bash
# Check the content of Marker.types.ts
cat src/components/common/Marker/Marker.types.ts

Length of output: 350

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

🧹 Outside diff range and nitpick comments (1)
src/constants/categories.ts (1)

1-10: Add JSDoc documentation for better maintainability.

The categories are well-defined and the as const assertion ensures type safety. Consider adding documentation to explain the purpose and usage of these categories.

+/**
+ * Predefined categories for map markers.
+ * Used by the Marker component to determine the icon and style of map markers.
+ */
 export const MARKER_CATEGORIES = [
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3d2e9a0 and 9e98806.

📒 Files selected for processing (4)
  • src/components/common/Marker/Marker.stories.tsx (1 hunks)
  • src/components/common/Marker/Marker.types.ts (1 hunks)
  • src/components/common/Marker/index.tsx (1 hunks)
  • src/constants/categories.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/common/Marker/Marker.stories.tsx
  • src/components/common/Marker/Marker.types.ts
  • src/components/common/Marker/index.tsx
🧰 Additional context used

Comment on lines +1 to +11
export const MARKER_CATEGORIES = [
'restaurant',
'cafe',
'bar',
'shopping',
'travel',
'public',
'hospital',
'other',
] as const;
export type MarkerCategory = (typeof MARKER_CATEGORIES)[number];
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 enhancing the category structure for better maintainability.

While the current implementation is functional, consider structuring the categories as an object with metadata for better maintainability and flexibility.

interface CategoryMetadata {
  label: string;
  icon?: string;
  color?: string;
}

export const MARKER_CATEGORIES = {
  restaurant: { label: '음식점' },
  cafe: { label: '카페' },
  bar: { label: '바' },
  shopping: { label: '쇼핑' },
  travel: { label: '여행' },
  public: { label: '공공시설' },
  hospital: { label: '병원' },
  other: { label: '기타' },
} as const;

export type MarkerCategory = keyof typeof MARKER_CATEGORIES;

This structure would:

  1. Allow for additional metadata per category
  2. Provide human-readable labels
  3. Support future extensions (icons, colors, etc.)
  4. Maintain type safety

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

Successfully merging this pull request may close these issues.

[FEAT] Marker 컴포넌트, 스토리북
2 participants