mirror of https://github.com/buster-so/buster.git
update rules around ui
This commit is contained in:
parent
2b100ed60b
commit
9f1c75dc5d
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './AppVerticalCodeSplitter';
|
|
@ -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
|
||||
}
|
||||
};
|
|
@ -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';
|
|
@ -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" />
|
|
@ -0,0 +1 @@
|
|||
export * from './AppVerticalSplitterWithGap';
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue