update rules around ui

This commit is contained in:
Nate Kelley 2025-02-27 08:38:44 -07:00
parent 2b100ed60b
commit 9f1c75dc5d
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
13 changed files with 118 additions and 52 deletions

View File

@ -1,6 +1,7 @@
---
description: Rules for the components ui directory
globs: src/components/features/**/*
alwaysApply: false
---
# Feature Components Rules
@ -14,8 +15,8 @@ Each feature component should be organized in its own directory using the follow
features/
└── ComponentName/
├── ComponentName.tsx # Main component implementation
├── ComponentName.types.ts # TypeScript interfaces and types
├── ComponentName.test.tsx # Tests for the component
├── ComponentName.test.tsx # Tests for the component (if asked for it)
├── ComponentName.stories.tsx # Storybook stories for the component
└── index.ts # Exports the component and its types
```
@ -26,11 +27,12 @@ features/
## Component Guidelines
1. Feature components should:
- Import basic UI components from `../ui/`
- Import basic UI components from `../ui/{component_directory}/{component_name}`
- Be functional components using React hooks
- Handle their own state management when necessary
- Be fully typed using TypeScript
- Include proper documentation and props interface
- Have a corresponding Storybook story showcasing different states and variations
2. Components should be:
- Reusable across different parts of the application

View File

@ -3,7 +3,6 @@ description: Rules for the components ui directory
globs: src/components/ui/**/*
alwaysApply: false
---
# Component Directory Structure and Organization
## Component Purpose
@ -13,7 +12,7 @@ alwaysApply: false
- The goal is to maintain a library of simple, pure components that can be easily reused across features
## Directory Organization
- Each directory within `src/components` should represent a specific category of components
- Each directory within `src/components/ui` should represent a specific category of components
- Directory names should clearly indicate the type of components contained within
- Examples:
- `buttons/` - Button components
@ -21,16 +20,21 @@ alwaysApply: false
- `card/` - Card and container components
- `icons/` - Icon components
- `layout/` - Layout-related components
- `text/` - Text and typography components
- `typography/` - Text and typography components
## Storybook
- Each component in this directory should have an assosciated storybook
- Each storybook story should try to include the args (props) that are assosciated with each component
- Storybook storys should be title UI/{directory}/{component_name}
## Component Dependencies and Imports
- When a component needs to use another component, prefer relative imports
- Example:
```typescript
// Good
import { IconButton } from '../buttons/ui/IconButton';
import { Button } from '../buttons/ui/buttons/Button';
```
- Exception: Only use absolute imports (@/) when the relative path would be overly complex (>3 levels deep)
- Exception: Only use absolute imports (@/) when the relative path would be overly complex (>4 levels deep)
## Styling Guidelines
1. Primary Styling Method:
@ -38,33 +42,12 @@ alwaysApply: false
- Class names should follow Tailwind's utility-first approach
2. Color Management:
- Always use `createStyles` from antd-style for color-related styling
- Access theme colors through the token system
- Example:
```typescript
const useStyles = createStyles(({ token, css }) => {
return {
container: css`
border: 0.5px solid ${token.colorBorderSecondary};
background: ${token.colorBgContainer};
`
};
});
```
3. Border Rules:
- When using borders with createStyles, always use 0.5px as the border width
- Access border colors through the token system
- Always use `tailwind.css` colors for color-related styling. Only use the colors defined in [tailwind.css](mdc:src/styles/tailwind.css). Do not use the other generic tailwind colors.
## Component Implementation
- Use functional components exclusively
- Implement proper TypeScript types for props and state
- Use React.memo() for performance optimization when appropriate
- Import text elements from dedicated components:
```typescript
import { Text } from '@/components/ui';
import { Title } from '@/components/ui';
```
##
I have a helper called `cn`. This is how I do a tailwind merge and classnames concatination. This is found in import { cn } from '@/lib/classMerge';

View File

@ -1,5 +1,6 @@
---
description: Rules for new stories
globs: src/components/**/*.stories.tsx
alwaysApply: false
---
All new stories title with "Base/{componentName}" unless specified otherwise.
All new stories title with "UI/directory/{componentName}" unless specified otherwise.

View File

@ -1,6 +1,7 @@
---
description: Rules for the context directory
globs: src/**/*.{ts,tsx}
alwaysApply: false
---
# Project: React TypeScript Application
@ -13,14 +14,12 @@ You are a TypeScript expert with deep knowledge of the language's features and b
- Use proper TypeScript types for all variables and functions
## Best Practices
- Try to use Ant Design componets (Button, Input, etc) over native HTML elements.
- If we need to create custom elements like cards or containers with border colors, use createStyles from antd-style and use the token to get the exact color
- If newly defined element has a border, it should be 0.5px
- If I am ever instead createStyles function from antd-style AND I am using a border, it should be 0.5px
- Try to use components defined in my @/components/ui folder.
- If we need to create custom elements, we should try to use tailwind to defined the component structure
- Use React.memo() for performance optimization when appropriate
- Prefer async/await over .then() for asynchronous operations
## ADDITIONAL PARAMS
- If we need to use useRouter, if needs to be from next/navigation
- If I need to use text, I want to import the <Text> component from the @/components/text, If I need to use a title, I want to import the <Title /> component from the @/components/text directory.
- If I need to use text, I want to import the <Text> component from the @/components/typography, If I need to use a title, I want to import the <Title /> component from the @/components/typography directory.

View File

@ -10,7 +10,7 @@ import { runSQL } from '@/api/buster_rest';
import type { RustApiError } from '@/api/buster_rest/errors';
import isEmpty from 'lodash/isEmpty';
import type { AppSplitterRef } from '@/components/ui/layout/AppSplitter';
import { AppVerticalCodeSplitter } from '@/components/ui/layout/AppVerticalCodeSplitter';
import { AppVerticalSplitterWithGap } from '@/components/ui/layout/AppVerticalSplitterWithGap';
import { useDatasetPageContextSelector } from '../_DatasetsLayout/DatasetPageContext';
export const EditorContent: React.FC<{
@ -73,7 +73,7 @@ export const EditorContent: React.FC<{
<EditorContainerSubHeader selectedApp={selectedApp} setSelectedApp={setSelectedApp} />
<div className={cx('h-full w-full overflow-hidden p-5', styles.container)}>
{selectedApp === EditorApps.PREVIEW && (
<AppVerticalCodeSplitter
<AppVerticalSplitterWithGap
autoSaveId="dataset-editor"
ref={splitterRef}
sql={sql}

View File

@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { AppSplitter } from './AppSplitter';
const meta = {
title: 'Base/Layout/AppSplitter',
title: 'Base/Layouts/AppSplitter',
component: AppSplitter,
parameters: {
layout: 'fullscreen'

View File

@ -1 +0,0 @@
export * from './AppVerticalCodeSplitter';

View File

@ -0,0 +1,75 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AppVerticalSplitterWithGap } from './AppVerticalSplitterWithGap';
const meta: Meta<typeof AppVerticalSplitterWithGap> = {
title: 'Base/Layouts/AppVerticalSplitterWithGap',
component: AppVerticalSplitterWithGap,
parameters: {
layout: 'fullscreen'
},
tags: ['autodocs']
};
export default meta;
type Story = StoryObj<typeof AppVerticalSplitterWithGap>;
const mockData: Record<string, string | number | null>[] = [
{ id: 1, name: 'Sample Data 1' },
{ id: 2, name: 'Sample Data 2' }
];
export const Default: Story = {
args: {
sql: 'SELECT * FROM sample_table',
setSQL: (sql: string) => console.log('SQL changed:', sql),
runSQLError: null,
onRunQuery: async () => console.log('Running query...'),
data: mockData,
fetchingData: false,
defaultLayout: ['50%', '50%'],
autoSaveId: 'default-splitter'
}
};
export const WithError: Story = {
args: {
...Default.args,
runSQLError: 'Invalid SQL syntax'
}
};
export const Loading: Story = {
args: {
...Default.args,
fetchingData: true
}
};
export const TopHidden: Story = {
args: {
...Default.args,
topHidden: true
}
};
export const CustomGap: Story = {
args: {
...Default.args,
gapAmount: 6
}
};
export const WithSaveButton: Story = {
args: {
...Default.args,
onSaveSQL: async () => console.log('Saving SQL...')
}
};
export const DisabledSave: Story = {
args: {
...Default.args,
onSaveSQL: async () => console.log('Saving SQL...'),
disabledSave: true
}
};

View File

@ -4,7 +4,7 @@ import { SQLContainer } from './SQLContainer';
import { DataContainer } from './DataContainer';
import type { IDataResult } from '@/api/asset_interfaces';
export interface AppVerticalCodeSplitterProps {
export interface AppVerticalSplitterWithGapProps {
sql: string;
setSQL: (sql: string) => void;
runSQLError: string | null;
@ -16,9 +16,13 @@ export interface AppVerticalCodeSplitterProps {
topHidden?: boolean;
onSaveSQL?: () => Promise<void>;
disabledSave?: boolean;
gapAmount?: number;
}
export const AppVerticalCodeSplitter = forwardRef<AppSplitterRef, AppVerticalCodeSplitterProps>(
export const AppVerticalSplitterWithGap = forwardRef<
AppSplitterRef,
AppVerticalSplitterWithGapProps
>(
(
{
sql,
@ -31,12 +35,14 @@ export const AppVerticalCodeSplitter = forwardRef<AppSplitterRef, AppVerticalCod
defaultLayout,
autoSaveId,
disabledSave = false,
topHidden = false
topHidden = false,
gapAmount = 3
},
ref
) => {
const sqlContainerClassName = !topHidden ? 'mb-3' : '';
const dataContainerClassName = !topHidden ? 'mt-3' : '';
//tailwind might not like this, but yolo
const sqlContainerClassName = !topHidden ? `mb-${gapAmount}` : '';
const dataContainerClassName = !topHidden ? `mt-${gapAmount}` : '';
return (
<AppSplitter
@ -71,4 +77,4 @@ export const AppVerticalCodeSplitter = forwardRef<AppSplitterRef, AppVerticalCod
}
);
AppVerticalCodeSplitter.displayName = 'AppVerticalCodeSplitter';
AppVerticalSplitterWithGap.displayName = 'AppVerticalSplitterWithGap';

View File

@ -6,15 +6,15 @@ import { Button, Divider } from 'antd';
import { createStyles } from 'antd-style';
import React, { useEffect, useMemo, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import type { AppVerticalCodeSplitterProps } from './AppVerticalCodeSplitter';
import type { AppVerticalSplitterWithGapProps } from './AppVerticalSplitterWithGap';
export const SQLContainer: React.FC<{
className?: string;
sql: string | undefined;
setDatasetSQL: (sql: string) => void;
onRunQuery: () => Promise<void>;
onSaveSQL?: AppVerticalCodeSplitterProps['onSaveSQL'];
disabledSave?: AppVerticalCodeSplitterProps['disabledSave'];
onSaveSQL?: AppVerticalSplitterWithGapProps['onSaveSQL'];
disabledSave?: AppVerticalSplitterWithGapProps['disabledSave'];
error?: string | null;
}> = React.memo(
({ disabledSave, className = '', sql, setDatasetSQL, onRunQuery, onSaveSQL, error }) => {
@ -101,7 +101,7 @@ const ErrorContainer: React.FC<{
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 0 }}
className={cx(styles.errorContainer, 'absolute bottom-full left-0 right-0 mx-4 mb-2')}>
className={cx(styles.errorContainer, 'absolute right-0 bottom-full left-0 mx-4 mb-2')}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<AppMaterialIcons icon="error" />

View File

@ -0,0 +1 @@
export * from './AppVerticalSplitterWithGap';

View File

@ -1,7 +1,7 @@
import React, { useEffect, useMemo } from 'react';
import type { MetricViewProps } from '../config';
import { useMetricIndividual } from '@/context/Metrics';
import { AppVerticalCodeSplitter } from '@/components/ui/layout/AppVerticalCodeSplitter';
import { AppVerticalSplitterWithGap } from '@/components/ui/layout/AppVerticalSplitterWithGap';
import { useMemoizedFn, useUnmount } from 'ahooks';
import { IDataResult } from '@/api/asset_interfaces';
import { useMetricLayout } from '../useMetricLayout';
@ -82,7 +82,7 @@ export const MetricViewResults: React.FC<MetricViewProps> = React.memo(({ metric
return (
<div ref={containerRef} className="h-full w-full p-3">
<AppVerticalCodeSplitter
<AppVerticalSplitterWithGap
ref={appSplitterRef}
autoSaveId={autoSaveId}
sql={sql}