Skip to content

Commit

Permalink
feat(addComponents): SimpleSelect, SimpleTableWithToolbar, SimplePlac…
Browse files Browse the repository at this point in the history
…eholder (#11)
  • Loading branch information
carlosthe19916 authored Nov 18, 2021
1 parent bc57577 commit 9da34f4
Show file tree
Hide file tree
Showing 12 changed files with 412 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/components/SimplePlaceholder/SimplePlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { Bullseye, Spinner } from '@patternfly/react-core';

export const SimplePlaceholder: React.FC = () => {
return (
<Bullseye>
<div className="pf-u-display-flex pf-u-flex-direction-column">
<div>
<Spinner />
</div>
<div className="pf-c-content">
<h3>Loading...</h3>
</div>
</div>
</Bullseye>
);
};
1 change: 1 addition & 0 deletions src/components/SimplePlaceholder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SimplePlaceholder';
62 changes: 62 additions & 0 deletions src/components/SimpleSelect/SimpleSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from 'react';

import {
Select,
SelectOption,
SelectOptionObject,
SelectOptionProps,
SelectProps,
} from '@patternfly/react-core';

export interface OptionWithValue<T = string> extends SelectOptionObject {
value: T;
props?: Partial<SelectOptionProps>; // Extra props for <SelectOption>, e.g. children, className
}

type OptionLike = string | SelectOptionObject | OptionWithValue;

export interface ISimpleSelectProps
extends Omit<
SelectProps,
'onChange' | 'isOpen' | 'onToggle' | 'onSelect' | 'selections' | 'value'
> {
'aria-label': string;
onChange: (selection: OptionLike) => void;
options: OptionLike[];
value?: OptionLike | OptionLike[];
}

export const SimpleSelect: React.FC<ISimpleSelectProps> = ({
onChange,
options,
value,
placeholderText = 'Select...',

...props
}) => {
const [isOpen, setIsOpen] = useState(false);

return (
<Select
placeholderText={placeholderText}
isOpen={isOpen}
onToggle={setIsOpen}
onSelect={(_, selection: OptionLike) => {
onChange(selection);
if (props.variant !== 'checkbox') {
setIsOpen(false);
}
}}
selections={value}
{...props}
>
{options.map((option, index) => (
<SelectOption
key={`${index}-${option.toString()}`}
value={option}
{...(typeof option === 'object' && (option as OptionWithValue).props)}
/>
))}
</Select>
);
};
1 change: 1 addition & 0 deletions src/components/SimpleSelect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SimpleSelect';
58 changes: 58 additions & 0 deletions src/components/SimpleTableWithToolbar/SimplePagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';

import { Pagination, PaginationVariant, ToggleTemplate } from '@patternfly/react-core';

export interface ISimplePaginationProps {
count: number;
params: {
perPage?: number;
page?: number;
};

isTop?: boolean;
isCompact?: boolean;
perPageOptions?: number[];
onChange: ({ page, perPage }: { page: number; perPage: number }) => void;
}

export const SimplePagination: React.FC<ISimplePaginationProps> = ({
count,
params,
isTop,
isCompact,
perPageOptions,
onChange,
}) => {
const mapPerPageOptions = (options: number[]) => {
return options.map((option) => ({
title: String(option),
value: option,
}));
};

const getPerPage = () => {
return params.perPage || 10;
};

return (
<Pagination
itemCount={count}
page={params.page || 1}
perPage={getPerPage()}
onPageInput={(_, page) => {
onChange({ page, perPage: getPerPage() });
}}
onSetPage={(_, page) => {
onChange({ page, perPage: getPerPage() });
}}
onPerPageSelect={(_, perPage) => {
onChange({ page: 1, perPage });
}}
isCompact={isTop || isCompact}
widgetId="pagination-options-menu"
variant={isTop ? PaginationVariant.top : PaginationVariant.bottom}
perPageOptions={mapPerPageOptions(perPageOptions || [10, 20, 50, 100])}
toggleTemplate={(props) => <ToggleTemplate {...props} />}
/>
);
};
24 changes: 24 additions & 0 deletions src/components/SimpleTableWithToolbar/SimpleStateError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import {
EmptyState,
EmptyStateIcon,
EmptyStateVariant,
Title,
EmptyStateBody,
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { global_danger_color_200 as globalDangerColor200 } from '@patternfly/react-tokens';

export const SimpleStateError: React.FC = () => {
return (
<EmptyState variant={EmptyStateVariant.small}>
<EmptyStateIcon icon={ExclamationCircleIcon} color={globalDangerColor200.value} />
<Title headingLevel="h2" size="lg">
Unable to connect
</Title>
<EmptyStateBody>
There was an error retrieving data. Check your connection and try again.
</EmptyStateBody>
</EmptyState>
);
};
21 changes: 21 additions & 0 deletions src/components/SimpleTableWithToolbar/SimpleStateNoData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import {
EmptyState,
EmptyStateBody,
EmptyStateIcon,
EmptyStateVariant,
Title,
} from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons';

export const SimpleStateNoData: React.FC = () => {
return (
<EmptyState variant={EmptyStateVariant.small}>
<EmptyStateIcon icon={CubesIcon} />
<Title headingLevel="h2" size="lg">
No data available
</Title>
<EmptyStateBody>No data available to be shown here.</EmptyStateBody>
</EmptyState>
);
};
25 changes: 25 additions & 0 deletions src/components/SimpleTableWithToolbar/SimpleStateNoResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import {
EmptyState,
EmptyStateBody,
EmptyStateIcon,
EmptyStateVariant,
Title,
} from '@patternfly/react-core';
import { SearchIcon } from '@patternfly/react-icons';

export const SimpleStateNoResults: React.FC = () => {
return (
<EmptyState variant={EmptyStateVariant.small}>
<EmptyStateIcon icon={SearchIcon} />
<Title headingLevel="h2" size="lg">
No results found
</Title>
<EmptyStateBody>
No results match the filter criteria. Remove all filters or clear all filters to show
results.
</EmptyStateBody>
</EmptyState>
);
};
112 changes: 112 additions & 0 deletions src/components/SimpleTableWithToolbar/SimpleTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React from 'react';
import { Bullseye, Spinner, Skeleton } from '@patternfly/react-core';
import { Table, TableHeader, TableBody, IRow, TableProps } from '@patternfly/react-table';

import { SimpleStateNoData } from './SimpleStateNoData';
import { SimpleStateNoResults } from './SimpleStateNoResults';
import { SimpleStateError } from './SimpleStateError';

export interface ISimpleTableProps extends TableProps {
isLoading: boolean;
loadingVariant?: 'skeleton' | 'spinner' | 'none';
fetchError?: any;

filtersApplied: boolean;
noDataState?: any;
noSearchResultsState?: any;
errorState?: any;
}

export const SimpleTable: React.FC<ISimpleTableProps> = ({
cells,
rows,
'aria-label': ariaLabel = 'main-table',

isLoading,
fetchError,
loadingVariant = 'skeleton',

filtersApplied,
noDataState,
noSearchResultsState,
errorState,

...rest
}) => {
if (isLoading && loadingVariant !== 'none') {
let rows: IRow[] = [];
if (loadingVariant === 'skeleton') {
rows = [...Array(10)].map(() => {
return {
cells: [...Array(cells.length)].map(() => ({
title: <Skeleton />,
})),
};
});
} else if (loadingVariant === 'spinner') {
rows = [
{
heightAuto: true,
cells: [
{
props: { colSpan: 8 },
title: (
<Bullseye>
<Spinner size="xl" />
</Bullseye>
),
},
],
},
];
} else {
throw new Error('Can not determine the loading state of table');
}

return (
<Table aria-label={ariaLabel} cells={cells} rows={rows}>
<TableHeader />
<TableBody />
</Table>
);
}

if (fetchError) {
return (
<>
<Table aria-label={ariaLabel} cells={cells} rows={[]}>
<TableHeader />
<TableBody />
</Table>
{errorState ? errorState : <SimpleStateError />}
</>
);
}

if (rows.length === 0) {
return filtersApplied ? (
<>
<Table aria-label={ariaLabel} cells={cells} rows={[]}>
<TableHeader />
<TableBody />
</Table>
{noSearchResultsState ? noSearchResultsState : <SimpleStateNoResults />}
</>
) : (
<>
<Table aria-label={ariaLabel} cells={cells} rows={[]}>
<TableHeader />
<TableBody />
</Table>
{noDataState ? noDataState : <SimpleStateNoData />}
</>
);
}

return (
<Table aria-label={ariaLabel} cells={cells} rows={rows} {...rest}>
<TableHeader />
<TableBody />
</Table>
);
};
Loading

0 comments on commit 9da34f4

Please sign in to comment.