Skip to content

Commit

Permalink
feat(react): change InstanceTable -> InstanceList, refreshInterval
Browse files Browse the repository at this point in the history
  • Loading branch information
jrea committed Oct 6, 2022
1 parent 34efa38 commit 4a68661
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 80 deletions.
2 changes: 1 addition & 1 deletion packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function App() {
[SignUpForm](./src/components/SignUpForm/README.md)
[InstanceTable](./src/components/InstanceTable/README.md)
[InstanceList](./src/components/InstanceList/README.md)
[Metrics](./src/components/Metrics/README.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Entity, Instance, Organization } from '@theniledev/js';
import { TableWrapper, TableSkeleton } from '../../lib/table';
import { flattenSchema, flatten } from '../../lib/utils/schema';

import { InstanceTableProps } from './types';
import { InstanceListProps } from './types';

type Props = Omit<InstanceTableProps, 'org'> & {
type Props = Omit<InstanceListProps, 'org'> & {
instances: void | Instance[];
entityData: void | Entity;
organization: void | Organization;
Expand All @@ -22,7 +22,7 @@ export const generateHeaderRow = (
entityData: void | Entity,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
processColumns?: (header: string, flatSchema: any) => GridColDef
) => {
): GridColDef[] => {
const flatSchema = entityData && flattenSchema(entityData?.schema, true);
if (flatSchema) {
const baseArr = Object.keys(flatSchema).map((header) => {
Expand All @@ -47,9 +47,12 @@ export const generateHeaderRow = (
{}
);

return columns.map((col) => {
return colLookup[String(col)];
});
return columns.reduce((accum: GridColDef[], col) => {
if (colLookup[String(col)] != null) {
accum.push(colLookup[String(col)]);
}
return accum;
}, []);
}
if (additionalColumns && additionalColumns.length) {
return baseArr.concat(additionalColumns);
Expand All @@ -72,7 +75,7 @@ export const generateHeaderRow = (
return [];
};

const InstanceTable = React.memo(function InstanceTable(props: Props) {
const InstanceList = React.memo(function InstanceList(props: Props) {
const {
isFetching,
instances,
Expand Down Expand Up @@ -183,4 +186,4 @@ const InstanceTable = React.memo(function InstanceTable(props: Props) {
</TableSkeleton>
);
});
export default InstanceTable;
export default InstanceList;
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import React from 'react';

import { useInterval } from '../../lib/hooks/useInterval';
import { useNile } from '../../context';
import Queries, { useQuery } from '../../lib/queries';

import InstanceTable from './InstanceTable';
import { InstanceTableProps } from './types';
import InstanceList from './InstanceList';
import { InstanceListProps, ComponentProps } from './types';

export type InstanceTableDataFetcherProps = InstanceTableProps;
export default function InstanceTableDataFetcher(
props: InstanceTableDataFetcherProps
export type InstanceListDataFetcherProps = InstanceListProps & {
refreshInterval?: number;
Component: ComponentProps;
};

export default function InstanceListDataFetcher(
props: InstanceListDataFetcherProps
) {
const {
entity,
Expand All @@ -22,6 +27,8 @@ export default function InstanceTableDataFetcher(
useQuery: customUseQuery,
processColumns,
actionButtons,
refreshInterval,
Component = InstanceList,
} = props;
const nile = useNile();
const useQueryHook = customUseQuery ?? useQuery;
Expand All @@ -36,16 +43,21 @@ export default function InstanceTableDataFetcher(
() => nile.entities.getEntity({ type: String(entity) })
);

const { data: instances, isFetching: isInstancesFetching } = useQueryHook(
Queries.ListInstances(entity, org),
() =>
nile.entities.listInstances({
type: String(entity),
org: String(org),
})
const {
refetch,
data: instances,
isFetching: isInstancesFetching,
} = useQueryHook(Queries.ListInstances(entity, org), () =>
nile.entities.listInstances({
type: String(entity),
org: String(org),
})
);

useInterval(refetch, refreshInterval);

return (
<InstanceTable
<Component
instances={instances}
entityData={entityData}
organization={organization}
Expand Down
53 changes: 53 additions & 0 deletions packages/react/src/components/InstanceList/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Instance Table

A list (defaults to a table based on [data grid table in mui](https://mui.com/x/react-data-grid/)) for rendering instances.

The default exports transparently handles request and loading state.

## Usage

```typescript
import { InstanceList, NileProvider } from '@theniledev/react';

const API_URL = 'http://localhost:8080'; // location of the Nile endpoint

function App() {
return (
<NileProvider basePath={API_URL}>
<InstanceList entity="myEntity" org="userOrg" />
</NileProvider>
);
}
```

## Customization

See the [InstanceList storybook](https://react-storybook-ten.vercel.app/?path=/story/InstanceList--default) for samples on customizing `<InstanceList />` to fit your needs.

If you need full control over rendering, you can pass a `Component` prop. `<InstanceList />` will do the data fetching, and pass props back to you.

```typescript
import { InstanceList, NileProvider, ComponentProps } from '@theniledev/react';

const API_URL = 'http://localhost:8080'; // location of the Nile endpoint

function HandleEverything(props: ComponentProps) {
return <>{JSON.stringify(props)}</>;
}

function App() {
return (
<NileProvider basePath={API_URL}>
<InstanceList
entity="myEntity"
org="userOrg"
Component={HandleEverything}
/>
</NileProvider>
);
}
```

## Theming

[theming](../../../README.md#UI%20customization)
3 changes: 3 additions & 0 deletions packages/react/src/components/InstanceList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { default as InstanceList } from './InstanceListDataFetcher';

export default InstanceList;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { GridRowParams, GridColDef } from '@mui/x-data-grid';
import { useQuery } from '@tanstack/react-query';
import { Instance, Organization } from '@theniledev/js';

export interface InstanceTableProps {
export interface InstanceListProps {
entity: string;
org: string;
handleRowClick?: (params: GridRowParams) => void;
Expand All @@ -22,3 +22,7 @@ export interface InstanceTableProps {
organization: Organization;
}) => React.ReactNode;
}

export type ComponentProps = React.FunctionComponent<
Omit<InstanceListProps, 'entity' | 'org'>
>;
29 changes: 0 additions & 29 deletions packages/react/src/components/InstanceTable/README.md

This file was deleted.

3 changes: 0 additions & 3 deletions packages/react/src/components/InstanceTable/index.tsx

This file was deleted.

17 changes: 1 addition & 16 deletions packages/react/src/components/Metrics/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {

import { useNile } from '../../context';
import Queries, { useQuery } from '../../lib/queries';
import { useInterval } from '../../lib/hooks/useInterval';

type UseMetricsReturn = {
isLoading: boolean;
Expand Down Expand Up @@ -76,19 +77,3 @@ export const useMetrics = (

return { isLoading, metrics: flatMetrics };
};

export const useInterval = (cb: () => void, delay: void | number) => {
const savedCallback = React.useRef(cb);

React.useEffect(() => {
savedCallback.current = cb;
}, [cb]);

React.useEffect(() => {
if (!delay) {
return;
}
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
};
2 changes: 1 addition & 1 deletion packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { default as LoginForm } from './components/LoginForm';
export { default as SignUpForm } from './components/SignUpForm';
export { default as InstanceTable } from './components/InstanceTable';
export { default as InstanceList } from './components/InstanceList';
export { default as EntityForm } from './components/EntityForm';
export { NileProvider, useNile } from './context';
export {
Expand Down
17 changes: 17 additions & 0 deletions packages/react/src/lib/hooks/useInterval/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

export const useInterval = (cb: () => void, delay: void | number) => {
const savedCallback = React.useRef(cb);

React.useEffect(() => {
savedCallback.current = cb;
}, [cb]);

React.useEffect(() => {
if (!delay) {
return;
}
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { GridRenderCellParams } from '@mui/x-data-grid';
import { Add } from '@mui/icons-material';
import Card from '@mui/joy/Card';

import InstanceTable from '../src/components/InstanceTable/InstanceTable';
import InstanceList from '../src/components/InstanceList/InstanceList';
import { NileProvider } from '../src/context';

const entityData = {
Expand Down Expand Up @@ -61,7 +61,7 @@ const instances: Instance[] = [
},
];
const meta: Meta = {
component: InstanceTable,
component: InstanceList,
argTypes: {
isFetching: {
description:
Expand Down Expand Up @@ -93,7 +93,7 @@ type StoryProps = {
const Template: Story<StoryProps> = (args) => {
return (
<NileProvider basePath="http://localhost:8080">
<InstanceTable
<InstanceList
entity="myEntity"
isFetching={args.isFetching}
instances={args.renderEmpty ? [] : instances}
Expand All @@ -118,7 +118,7 @@ Default.args = {

const EmptyState = () => (
<NileProvider basePath="http://localhost:8080">
<InstanceTable
<InstanceList
entity="myEntity"
isFetching={false}
instances={[]}
Expand Down Expand Up @@ -164,7 +164,7 @@ const RenderEventLink = (props: GridRenderCellParams<Instance>) => {

const Columns = () => (
<NileProvider basePath="http://localhost:8080">
<InstanceTable
<InstanceList
entity="myEntity"
isFetching={false}
instances={instances}
Expand All @@ -188,7 +188,7 @@ export const FilteredAndCustomColumns = Columns.bind({});

const ActionButtons = () => (
<NileProvider basePath="http://localhost:8080">
<InstanceTable
<InstanceList
entity="myEntity"
isFetching={false}
instances={instances}
Expand Down Expand Up @@ -216,7 +216,7 @@ export const TableActionButtons = ActionButtons.bind({});

const Cards = () => (
<NileProvider basePath="http://localhost:8080">
<InstanceTable
<InstanceList
entity="myEntity"
isFetching={false}
instances={instances.concat([
Expand Down
11 changes: 10 additions & 1 deletion packages/react/test/components/InstanceTable/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { generateHeaderRow } from '../../../src/components/InstanceTable/InstanceTable';
import { generateHeaderRow } from '../../../src/components/InstanceList/InstanceList';

describe('instance table', () => {
let entityData: any;
Expand Down Expand Up @@ -43,4 +43,13 @@ describe('instance table', () => {
{ field: 'familyName', flex: 1, headerName: 'familyName', minWidth: 200 },
]);
});

it('handles columns that do not exist', () => {
const additionalColumns: any = [];
const columns: any = ['not here', 'familyName'];
const headerRow = generateHeaderRow(additionalColumns, columns, entityData);
expect(headerRow).toEqual([
{ field: 'familyName', flex: 1, headerName: 'familyName', minWidth: 200 },
]);
});
});

0 comments on commit 4a68661

Please sign in to comment.