Skip to content

Commit

Permalink
Output SQL console results in react-table (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
joncombe authored Sep 19, 2024
1 parent 7f40ad8 commit 2bc07ff
Show file tree
Hide file tree
Showing 20 changed files with 707 additions and 344 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

## Unreleased

## 2024-09-19 - 0.13.0

- Add scroll margin to SQL console editor.
- Bump sqlparse to 0.0.6.
- Improve the UX of the SQL results output.

## 2024-09-05 - 0.12.1

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@crate.io/crate-gc-admin",
"version": "0.12.1",
"version": "0.13.0",
"author": "crate.io",
"private": false,
"type": "module",
Expand Down Expand Up @@ -36,6 +36,7 @@
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.1.0",
"@tanstack/match-sorter-utils": "^8.11.8",
"@tanstack/react-table": "^8.11.8",
"ace-builds": "^1.32.2",
Expand Down Expand Up @@ -75,6 +76,7 @@
"start": "vite",
"build": "tsc && vite build",
"build-lib": "tsc && vite build --config vite.config.lib.ts",
"check-types": "tsc --noemit",
"prepack": "yarn run build-lib",
"test": "jest",
"lint": "eslint --cache --ext=.ts --ext=.tsx src/",
Expand Down
85 changes: 85 additions & 0 deletions src/components/CrateTabsShad/CrateTabsShad.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { render, screen } from 'test/testUtils';
import CrateTabsShad, { CrateTabsShadProps } from './CrateTabsShad';

const defaultProps: CrateTabsShadProps = {
items: [
{
key: 'tab1',
label: 'Tab 1',
content: <div data-testid="tab_1_content">tab 1</div>,
},
{
key: 'tab2',
label: 'Tab 2',
content: <div data-testid="tab_2_content">tab 2</div>,
},
{
key: 'tab3',
label: 'Tab 3',
content: <div data-testid="tab_3_content">tab 3</div>,
},
],
};

const setup = (props: Partial<CrateTabsShadProps> = {}) => {
const combinedProps = { ...defaultProps, ...props };

return render(<CrateTabsShad {...combinedProps} />);
};

describe('The CrateTabsShad component', () => {
it('preselects the first tab if no initialActiveTab prop is passed', () => {
setup();

expect(screen.getAllByRole('tab')[0].getAttribute('aria-selected')).toBe('true');
expect(screen.getAllByRole('tab')[1].getAttribute('aria-selected')).toBe(
'false',
);
expect(screen.getAllByRole('tab')[2].getAttribute('aria-selected')).toBe(
'false',
);
});

it('preselects the tab using the value of the initialActiveTab prop', () => {
setup({ initialActiveTab: 'tab2' });

expect(screen.getAllByRole('tab')[0].getAttribute('aria-selected')).toBe(
'false',
);
expect(screen.getAllByRole('tab')[1].getAttribute('aria-selected')).toBe('true');
expect(screen.getAllByRole('tab')[2].getAttribute('aria-selected')).toBe(
'false',
);
});

describe('when the hideWhenSingleTab prop is true', () => {
it('displays the tabs when there are multiple tabs', () => {
setup({ hideWhenSingleTab: true });

expect(screen.getAllByRole('tab').length).toBe(3);
});

it('hides the tabs when there is only one', () => {
setup({
hideWhenSingleTab: true,
items: [
{
key: 'tab1',
label: 'Tab 1',
content: <div data-testid="tab_1_content">tab 1</div>,
},
],
});

expect(screen.getByRole('tablist')).toHaveClass('hidden');
});
});

describe('when the stickyTabBar prop is true', () => {
it('applies the sticky CSS classes to the tablist', () => {
setup({ stickyTabBar: true });

expect(screen.getByTestId('tabs-container')).toHaveClass('flex');
});
});
});
93 changes: 93 additions & 0 deletions src/components/CrateTabsShad/CrateTabsShad.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from 'react';
import * as TabsPrimitive from '@radix-ui/react-tabs';

import { cn } from 'utils';

export type CrateTabShadProps = {
key: string;
label: React.ReactNode | string;
content: React.ReactNode;
};

export type CrateTabsShadProps = {
initialActiveTab?: string;
hideWhenSingleTab?: boolean; // don't show the tab bar if there is only one tab
items: CrateTabShadProps[];
stickyTabBar?: boolean; // make the tab bar sticky
};

function CrateTabsShad({
initialActiveTab,
hideWhenSingleTab = false,
items = [],
stickyTabBar = false,
}: CrateTabsShadProps) {
return (
<Tabs
defaultValue={initialActiveTab || items[0].key}
className={stickyTabBar ? 'flex h-full w-full flex-col' : ''}
data-testid="tabs-container"
>
<TabsList
className={hideWhenSingleTab && items.length == 1 ? 'hidden' : 'border-b'}
>
{items.map(item => (
<TabsTrigger key={item.key} value={item.key}>
{item.label}
</TabsTrigger>
))}
</TabsList>
{items.map(item => (
<TabsContent
key={item.key}
value={item.key}
className={stickyTabBar ? 'h-full w-full overflow-hidden' : ''}
>
{item.content}
</TabsContent>
))}
</Tabs>
);
}

// below here are the shadcn components, with only the tailwind class names
// changed for our own use case

const Tabs = TabsPrimitive.Root;

const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn('flex h-10 justify-start gap-4 px-2', className)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;

const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
'border-b-2 border-transparent data-[state=active]:border-crate-blue',
className,
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;

const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content ref={ref} className={cn('', className)} {...props} />
));
TabsContent.displayName = TabsPrimitive.Content.displayName;

export default CrateTabsShad;
3 changes: 3 additions & 0 deletions src/components/CrateTabsShad/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import CrateTabsShad from './CrateTabsShad';

export default CrateTabsShad;
45 changes: 45 additions & 0 deletions src/components/DataTable/DataTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,51 @@ describe('The DataTable component', () => {
// There should be only 10 table rows per page
expect(getNumberOfRows(container)).toBe(10);
});

describe('when hidePaginationWhenSinglePage is set', () => {
it('does not display pagination if there is only one page', () => {
setup({
data: generateData(1),
hidePaginationWhenSinglePage: true,
});

expect(screen.queryByTestId('datatable-pagination')).not.toBeInTheDocument();
});
});

describe('when hidePaginationPageSize is set', () => {
it('does not display the page size selector', () => {
setup({
hidePaginationPageSize: true,
});

expect(screen.queryByTestId('datatable-pagination')).toBeInTheDocument();

expect(
screen.queryByTestId('datatable-pagination-page-size'),
).not.toBeInTheDocument();
});
});

describe('when paginationContent is set', () => {
it('displays the custom pagination content', () => {
setup({
paginationContent: <div data-testid="custom-pagination-content" />,
});

expect(screen.getByTestId('custom-pagination-content')).toBeInTheDocument();
});
});
});

describe('when stickyHeader is set', () => {
it('applies the CSS classes to make the table header sticky', () => {
setup({
stickyHeader: true,
});

expect(screen.getByTestId('head_col_name')).toHaveClass('sticky');
});
});

describe('when table is empty', () => {
Expand Down
Loading

0 comments on commit 2bc07ff

Please sign in to comment.