-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add FilterCard component #653
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
base: main
Are you sure you want to change the base?
Changes from all commits
8a676c8
bac9f7a
661db9b
380e53b
77cbe32
0e02d80
a320f85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| 'use client'; | ||
|
|
||
| import { Button, FilterCard, Select, Text } from '@raystack/apsara'; | ||
| import PlaygroundLayout from './playground-layout'; | ||
|
|
||
| export function FilterCardExamples() { | ||
| return ( | ||
| <PlaygroundLayout title='FilterCard'> | ||
| <FilterCard> | ||
| <FilterCard.Section> | ||
| <FilterCard.Item label='Ordering'> | ||
| <Select defaultValue='auto'> | ||
| <Select.Trigger | ||
| size='small' | ||
| variant='outline' | ||
| style={{ width: '100%' }} | ||
| > | ||
| <Select.Value /> | ||
| </Select.Trigger> | ||
| <Select.Content> | ||
| <Select.Item value='auto'>Auto</Select.Item> | ||
| <Select.Item value='name'>Name</Select.Item> | ||
| <Select.Item value='date'>Date</Select.Item> | ||
| </Select.Content> | ||
| </Select> | ||
| </FilterCard.Item> | ||
| <FilterCard.Item label='Grouping'> | ||
| <Select defaultValue='none'> | ||
| <Select.Trigger | ||
| size='small' | ||
| variant='outline' | ||
| style={{ width: '100%' }} | ||
| > | ||
| <Select.Value /> | ||
| </Select.Trigger> | ||
| <Select.Content> | ||
| <Select.Item value='none'>No grouping</Select.Item> | ||
| <Select.Item value='status'>Status</Select.Item> | ||
| <Select.Item value='type'>Type</Select.Item> | ||
| </Select.Content> | ||
| </Select> | ||
| </FilterCard.Item> | ||
| </FilterCard.Section> | ||
| <FilterCard.Section title='Display Properties'> | ||
| <Text size={1}>Name, Creator, Project</Text> | ||
| </FilterCard.Section> | ||
| <FilterCard.Footer> | ||
| <Button variant='text' size='small' color='neutral'> | ||
| Reset to default | ||
| </Button> | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </FilterCard.Footer> | ||
| </FilterCard> | ||
| </PlaygroundLayout> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| 'use client'; | ||
|
|
||
| import { getPropsString } from '@/lib/utils'; | ||
|
|
||
| export const getCode = (props: any) => { | ||
| const { children, ...rest } = props; | ||
| return `<FilterCard> | ||
| <FilterCard.Section${getPropsString(rest)}>${children}</FilterCard.Section> | ||
| </FilterCard>`; | ||
| }; | ||
|
Comment on lines
+5
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unescaped
Additionally, since 🛡️ Proposed fix-export const getCode = (props: any) => {
+export const getCode = (props: { title?: string; children?: string }) => {
const { children, ...rest } = props;
+ const safeChildren = (children ?? '').replace(/</g, '<').replace(/>/g, '>');
return `<FilterCard>
- <FilterCard.Section${getPropsString(rest)}>${children}</FilterCard.Section>
+ <FilterCard.Section${getPropsString(rest)}>${safeChildren}</FilterCard.Section>
</FilterCard>`;
};🤖 Prompt for AI Agents |
||
|
|
||
| export const playground = { | ||
| type: 'playground', | ||
| controls: { | ||
| title: { | ||
| type: 'text', | ||
| initialValue: 'Display Properties' | ||
| }, | ||
| children: { | ||
| type: 'text', | ||
| initialValue: 'Section content goes here' | ||
| } | ||
| }, | ||
| getCode | ||
| }; | ||
|
|
||
| export const sectionsDemo = { | ||
| type: 'code', | ||
| code: ` | ||
| <FilterCard> | ||
| <FilterCard.Section> | ||
| <FilterCard.Item label="Ordering"> | ||
| <Select defaultValue="auto"> | ||
| <Select.Trigger size="small" variant="outline" style={{ width: "100%" }}> | ||
| <Select.Value /> | ||
| </Select.Trigger> | ||
| <Select.Content> | ||
| <Select.Item value="auto">Auto</Select.Item> | ||
| <Select.Item value="name">Name</Select.Item> | ||
| <Select.Item value="date">Date</Select.Item> | ||
| </Select.Content> | ||
| </Select> | ||
| </FilterCard.Item> | ||
| <FilterCard.Item label="Grouping"> | ||
| <Select defaultValue="none"> | ||
| <Select.Trigger size="small" variant="outline" style={{ width: "100%" }}> | ||
| <Select.Value /> | ||
| </Select.Trigger> | ||
| <Select.Content> | ||
| <Select.Item value="none">No grouping</Select.Item> | ||
| <Select.Item value="status">Status</Select.Item> | ||
| <Select.Item value="type">Type</Select.Item> | ||
| </Select.Content> | ||
| </Select> | ||
| </FilterCard.Item> | ||
| </FilterCard.Section> | ||
| <FilterCard.Section title="Display Properties"> | ||
| <Text size={1}>Name, Creator, Project</Text> | ||
| </FilterCard.Section> | ||
| <FilterCard.Footer> | ||
| <Button variant="text" size="small" color="neutral">Reset to default</Button> | ||
| </FilterCard.Footer> | ||
| </FilterCard>` | ||
| }; | ||
|
|
||
| export const withoutTitleDemo = { | ||
| type: 'code', | ||
| code: ` | ||
| <FilterCard> | ||
| <FilterCard.Section> | ||
| <Text size={1}>Section without a title</Text> | ||
| </FilterCard.Section> | ||
| </FilterCard>` | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| --- | ||
| title: FilterCard | ||
| description: A sectioned card component for grouping filter controls and configuration options. | ||
| source: packages/raystack/components/filter-card | ||
| --- | ||
|
|
||
| import { playground, sectionsDemo, withoutTitleDemo } from "./demo.ts"; | ||
|
|
||
| <Demo data={playground} /> | ||
|
|
||
| ## Anatomy | ||
|
|
||
| Import and assemble the component: | ||
|
|
||
| ```tsx | ||
| import { FilterCard } from '@raystack/apsara' | ||
|
|
||
| <FilterCard> | ||
| <FilterCard.Section> | ||
| <FilterCard.Item label="Label"> | ||
| Action content | ||
| </FilterCard.Item> | ||
| </FilterCard.Section> | ||
| <FilterCard.Section title="Section Title"> | ||
| Content | ||
| </FilterCard.Section> | ||
| <FilterCard.Footer> | ||
| Footer content | ||
| </FilterCard.Footer> | ||
| </FilterCard> | ||
| ``` | ||
|
|
||
| ## API Reference | ||
|
|
||
| ### FilterCard | ||
|
|
||
| The outer container for the filter card. | ||
|
|
||
| <auto-type-table path="./props.ts" name="FilterCardProps" /> | ||
|
|
||
| ### FilterCard.Section | ||
|
|
||
| A section within the filter card with an optional title. | ||
|
|
||
| <auto-type-table path="./props.ts" name="FilterCardSectionProps" /> | ||
|
|
||
| ### FilterCard.Item | ||
|
|
||
| A label-action row within a section. | ||
|
|
||
| <auto-type-table path="./props.ts" name="FilterCardItemProps" /> | ||
|
|
||
| ### FilterCard.Footer | ||
|
|
||
| A compact footer section, right-aligned by default. | ||
|
|
||
| <auto-type-table path="./props.ts" name="FilterCardFooterProps" /> | ||
|
|
||
| ## Examples | ||
|
|
||
| ### With Sections | ||
|
|
||
| Multiple sections separated by a divider: | ||
|
|
||
| <Demo data={sectionsDemo} /> | ||
|
|
||
| ### Without Title | ||
|
|
||
| Sections can be rendered without a title: | ||
|
|
||
| <Demo data={withoutTitleDemo} /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| export interface FilterCardProps { | ||
| /** Content of the filter card */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface FilterCardSectionProps { | ||
| /** Optional title for the section */ | ||
| title?: string; | ||
|
|
||
| /** Content of the section */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names */ | ||
| className?: string; | ||
|
|
||
| /** | ||
| * Flex direction | ||
| * @defaultValue "column" | ||
| */ | ||
| direction?: 'row' | 'column' | 'rowReverse' | 'columnReverse'; | ||
|
|
||
| /** Flex align items */ | ||
| align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline'; | ||
|
|
||
| /** Flex justify content */ | ||
| justify?: 'start' | 'center' | 'end' | 'between'; | ||
|
|
||
| /** Flex wrap */ | ||
| wrap?: 'noWrap' | 'wrap' | 'wrapReverse'; | ||
|
|
||
| /** Flex gap */ | ||
| gap?: | ||
| | 1 | ||
| | 2 | ||
| | 3 | ||
| | 4 | ||
| | 5 | ||
| | 6 | ||
| | 7 | ||
| | 8 | ||
| | 9 | ||
| | 'extra-small' | ||
| | 'small' | ||
| | 'medium' | ||
| | 'large' | ||
| | 'extra-large'; | ||
| } | ||
|
|
||
| export interface FilterCardItemProps { | ||
| /** Label text displayed on the left */ | ||
| label: string; | ||
|
|
||
| /** Action content displayed on the right */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface FilterCardFooterProps { | ||
| /** Content of the footer */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names */ | ||
| className?: string; | ||
|
|
||
| /** | ||
| * Flex justify content | ||
| * @defaultValue "end" | ||
| */ | ||
| justify?: 'start' | 'center' | 'end' | 'between'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: raystack/apsara
Length of output: 931
🏁 Script executed:
Repository: raystack/apsara
Length of output: 86
🏁 Script executed:
Repository: raystack/apsara
Length of output: 2303
🏁 Script executed:
Repository: raystack/apsara
Length of output: 138
🏁 Script executed:
Repository: raystack/apsara
Length of output: 146
🏁 Script executed:
Repository: raystack/apsara
Length of output: 198
🏁 Script executed:
Repository: raystack/apsara
Length of output: 229
🏁 Script executed:
Repository: raystack/apsara
Length of output: 86
🏁 Script executed:
Repository: raystack/apsara
Length of output: 86
🏁 Script executed:
Repository: raystack/apsara
Length of output: 2254
🏁 Script executed:
Repository: raystack/apsara
Length of output: 3701
🏁 Script executed:
Repository: raystack/apsara
Length of output: 638
🏁 Script executed:
Repository: raystack/apsara
Length of output: 2715
🏁 Script executed:
Repository: raystack/apsara
Length of output: 238
Remove
variant='filter'fromSelect.Trigger.Select.Triggeronly supportsvariant='outline'(default) orvariant='text'. The value'filter'is not a valid variant and will be ignored. In actual DataTable implementations (ordering.tsx,grouping.tsx), the filter styling is applied viadata-variant='filter'onSelect.Content, not on the trigger. Move or remove this prop from lines 15 and 31.Align Button variant with actual implementation.
Line 48 uses
variant='ghost', but the reset button in the actual DisplaySettings component (display-settings.tsxline 107) usesvariant='text' color='neutral'. Align the playground example with the production pattern.🤖 Prompt for AI Agents