Skip to content

Commit

Permalink
Init search
Browse files Browse the repository at this point in the history
  • Loading branch information
fhlavac committed Aug 23, 2024
1 parent 12b5bfd commit 299fff6
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ propComponents: ['DataView']
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Layout/Layout.md
---
import { useMemo } from 'react';
import { useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { useDataViewPagination, useDataViewSelection, useDataViewFilters } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect';
import DataView from '@patternfly/react-data-view/dist/dynamic/DataView';
import DataViewToolbar from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
import { FilterLabels } from '@patternfly/react-data-view/dist/dynamic/FilterLabels';

The **data view** component renders record data in a configured layout.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import { Pagination } from '@patternfly/react-core';
import { Pagination, SearchInput, ToolbarFilter } from '@patternfly/react-core';
import { Table, Tbody, Th, Thead, Tr, Td } from '@patternfly/react-table';
import { useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { useDataViewFilters, useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect';
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
Expand Down Expand Up @@ -43,6 +43,8 @@ export const BasicExample: React.FunctionComponent = () => {
const { page, perPage } = pagination;
const selection = useDataViewSelection({});
const { selected, onSelect, isSelected } = selection;
const filtersContext = useDataViewFilters({ name: '' });
const { filters, onSetFilters, onDeleteFilters } = filtersContext;

const pageData = useMemo(() => repositories.slice((page - 1) * perPage, ((page - 1) * perPage) + perPage), [ page, perPage ]);

Expand Down Expand Up @@ -74,7 +76,21 @@ export const BasicExample: React.FunctionComponent = () => {
itemCount={repositories.length}
{...pagination}
/>
}
}
search={
<ToolbarFilter
chips={[ { key: 'name', node: filters.name } ]}
deleteChip={(category, chip) => onSetFilters(undefined, { name: '' })}
deleteChipGroup={(category) => onSetFilters(undefined, { name: '' })}
categoryName="name"
>
<SearchInput
aria-label="Name filter"
onChange={(e, value) => onSetFilters(e, { name: value })}
value={filters.name}
onClear={(e) => onSetFilters(e, { name: '' })}
/></ToolbarFilter>
}
/>
<Table aria-label="Repositories table" ouiaId={ouiaId}>
<Thead data-ouia-component-id={`${ouiaId}-thead`}>
Expand Down
9 changes: 8 additions & 1 deletion packages/module/src/DataViewToolbar/DataViewToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ export interface DataViewToolbarProps extends PropsWithChildren {
bulkSelect?: React.ReactNode;
/** React component to display pagination */
pagination?: React.ReactNode;
/** React component to display search input */
search?: React.ReactNode;
}

export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, pagination, children, ...props }: DataViewToolbarProps) => (
export const DataViewToolbar: React.FC<DataViewToolbarProps> = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, search, pagination, children, ...props }: DataViewToolbarProps) => (
<Toolbar ouiaId={ouiaId} className={className} {...props}>
<ToolbarContent>
{bulkSelect && (
<ToolbarItem data-ouia-component-id={`${ouiaId}-bulk-select`}>
{bulkSelect}
</ToolbarItem>
)}
{search && (
<ToolbarItem variant={ToolbarItemVariant['search-filter']} data-ouia-component-id={`${ouiaId}-search`}>
{search}
</ToolbarItem>
)}
{pagination && (
<ToolbarItem variant={ToolbarItemVariant.pagination} data-ouia-component-id={`${ouiaId}-pagination`}>
{pagination}
Expand Down
139 changes: 139 additions & 0 deletions packages/module/src/FilterLabels/FilterLabels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from 'react';
import { Badge, Button, ButtonVariant, Chip, ChipGroup, ChipGroupProps, ChipProps } from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
import clsx from 'clsx';

export interface FilterLabel extends Omit<ChipProps, 'children'> {
/** The text of the filter label that will be displayed */
text: string;
/** Optional count associated with the filter label */
count?: number;
}

export interface FilterLabelGroup extends Omit<ChipGroupProps, 'children' | 'ref'> {
/** An array of filter labels that belong to the category */
labels: FilterLabel[];
}

export type FilterLabelsFilter = FilterLabel | FilterLabelGroup;

export interface FilterLabelsProps {
/** Additional class names to be applied to the FilterLabels component */
className?: string;
/** Array of filters that can be either individual labels or label groups */
filters: FilterLabelsFilter[];
/** Callback function invoked when one or multiple filters are removed */
onDelete: (event: React.MouseEvent<Element, MouseEvent>, filters: FilterLabelsFilter | FilterLabelsFilter[]) => void;
/** Custom title for the delete all button */
deleteAllButtonTitle?: React.ReactNode;
/** Determines whether to show the delete all button */
showDeleteAllButton?: boolean;
/** Determines whether to show the delete group button */
showDeleteGroupButton?: boolean;
/** FilterLabels OUIA ID */
ouiaId?: string | number;
}

// Filter label group type guard
export const isFilterLabelGroup = (group: FilterLabelsFilter): group is FilterLabelGroup => Object.prototype.hasOwnProperty.call(group, 'categoryName');

// Plain filter label type guard
export const isFilterLabel = (group: FilterLabelsFilter): group is FilterLabel => !isFilterLabelGroup(group);

const useStyles = createUseStyles({
chipFilters: {
'& .pf-v5-c-chip-group:not(:last-child)': {
marginRight: 'var(--pf-v5-global--spacer--sm)',
},
'& .pf-v5-c-chip .pf-v5-c-badge': {
marginLeft: 'var(--pf-v5-global--spacer--xs)',
},
},
});

export const FilterLabels: React.FunctionComponent<FilterLabelsProps> = ({
className,
filters,
onDelete,
deleteAllButtonTitle = 'Clear filters',
showDeleteAllButton = true,
showDeleteGroupButton = false,
ouiaId = 'FilterLabels'
}: FilterLabelsProps) => {
const classes = useStyles();
const groups: FilterLabelGroup[] = filters.filter(isFilterLabelGroup);

const groupedFilters = groups.map(({ labels, ...group }) => (
<ChipGroup
ouiaId={`${ouiaId}-group-${group.categoryName}`}
key={`group-${group.categoryName}`}
{...group}
{...(showDeleteGroupButton && labels.length > 1 && {
isClosable: true,
onClick: (event) => {
event.stopPropagation();
onDelete(
event,
{ ...group, labels }
);
},
})}
>
{labels.map((label: FilterLabel) => (
<Chip
ouiaId={`${ouiaId}-label-${group.categoryName}-${label.text}`}
key={label.text}
onClick={(event) => {
event.stopPropagation();
onDelete(event, { ...group, labels: [ label ] });
}}
>
{label.text}
{label.count && (
<Badge
key={`badge-${label.text}`}
data-ouia-component-id={`${ouiaId}-label-${group.categoryName}-${label.text}-badge`} isRead={label.isReadOnly}
>
{label.count}
</Badge>
)}
{label.badge}
</Chip>
))}
</ChipGroup>
));

const plainFilters = filters.filter(isFilterLabel);

return (
<span className={clsx(className, classes.chipFilters)}>
{groupedFilters}
{plainFilters?.map((label) => (
<ChipGroup key={`plain-label-${label.text}`} ouiaId={`${ouiaId}-group-${label.text}`}>
<Chip
ouiaId={`${ouiaId}-label-${label.text}`}
onClick={(event) => {
event.stopPropagation();
onDelete(event, [ label ]);
}}
>
{label.text}
{label.count && (
<Badge key={`label-badge-${label.text}`} data-ouia-component-id={`${ouiaId}-label-${label.text}-badge`} isRead={label.isReadOnly}>
{label.count}
</Badge>
)}
{label.badge}
</Chip>
</ChipGroup>
))}
{(showDeleteAllButton && filters.length > 0) && (
<Button variant={ButtonVariant.link} ouiaId={`${ouiaId}-delete-all-button`} onClick={(event) => onDelete(event, filters)}>
{deleteAllButtonTitle}
</Button>
)}
</span>
);
};

export default FilterLabels;
2 changes: 2 additions & 0 deletions packages/module/src/FilterLabels/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './FilterLabels';
export * from './FilterLabels';
3 changes: 3 additions & 0 deletions packages/module/src/Hooks/filters.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import '@testing-library/jest-dom';


25 changes: 25 additions & 0 deletions packages/module/src/Hooks/filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState } from "react";

export interface UseDataViewFiltersProps {
/** Array of initially selected entries */
initialFilters?: Record<string, any>;
}

export const useDataViewFilters = (props: UseDataViewFiltersProps) => {
const [ filters, setFilters ] = useState<Record<string, any>>(props.initialFilters ?? {});

const onSetFilters = (_event: React.FormEvent<HTMLInputElement> | undefined, newFilters: Record<string, any>) => {
setFilters(prev => ({ ...prev, ...newFilters }));
}

const onDeleteFilters = (_event: React.FormEvent<HTMLInputElement> | undefined, filtersToDelete: Record<string, any>) => {
setFilters(prev => ({ ...filtersToDelete }));
}

return {
filters,
onSetFilters,
onDeleteFilters
};
};
1 change: 1 addition & 0 deletions packages/module/src/Hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './pagination';
export * from './selection';
export * from './filters';
3 changes: 3 additions & 0 deletions packages/module/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// this file is autogenerated by generate-index.js, modifying it manually will have no effect
export * from './Hooks';

export { default as FilterLabels } from './FilterLabels';
export * from './FilterLabels';

export { default as DataViewToolbar } from './DataViewToolbar';
export * from './DataViewToolbar';

Expand Down

0 comments on commit 299fff6

Please sign in to comment.