# Common usage

This example demonstrates how the majority of developers will want to use the DataView package. It showcases a comprehensive implementation that integrates all the core features and capabilities of the data view system in a real-world scenario.

This example includes:

- **Sorting** - Click column headers to sort data in ascending or descending order
- **Filtering** - Multiple filter types including text filters (name, branch) and checkbox filters (workspace)
- **Pagination** - Navigate through large datasets with configurable page sizes
- **Selection** - Bulk selection with individual row selection, page selection, and "select all" functionality
- **Actions** - Row-level actions menu and toolbar actions for data management operations
- **Events** - Custom event handling for interactive features like row clicks and detail views
- **Empty states** - Graceful handling when no data matches current filters or queries
- **Detail drawer** - Expandable detail panel that opens when clicking on table rows
- **Data management** - Complete data lifecycle including filtering, sorting, and pagination with proper state management
- **Responsive design** - Actions and layout that adapt to different screen sizes
- **Accessibility** - Full ARIA support and keyboard navigation

This pattern provides a solid foundation for building feature-rich data tables and lists with minimal custom implementation required.

```jsx
import { FunctionComponent, useEffect, useState, useRef, useMemo } from 'react';
import { Drawer, DrawerActions, DrawerCloseButton, DrawerContent, DrawerContentBody, DrawerHead, DrawerPanelContent, Title, Content, EmptyState, EmptyStateBody, EmptyStateFooter, EmptyStateActions, Button,  } from '@patternfly/react-core';
import { ActionsColumn, Tbody, Td, ThProps, Tr } from '@patternfly/react-table';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect';
import { Pagination } from '@patternfly/react-core';
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
import { DataViewTable, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { DataViewEventsProvider, EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext';
import { useDataViewPagination, useDataViewSelection, useDataViewFilters, useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { ResponsiveAction, ResponsiveActions } from '@patternfly/react-component-groups';
import { DataViewFilterOption, DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters';
import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter';
import { DataViewCheckboxFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter';
import { CubesIcon } from '@patternfly/react-icons';

const perPageOptions = [
  { title: '5', value: 5 },
  { title: '10', value: 10 }
];

interface Repository {
  name: string;
  branch: string | null;
  prs: string | null;
  workspace: string;
  lastCommit: string;
};

interface RepositoryFilters {
  name: string,
  branch: string,
  workspace: string[]
};

const repositories: Repository[] = [
  { name: 'Repository one', branch: 'Branch one', prs: 'Pull request one', workspace: 'Workspace one', lastCommit: 'Timestamp one' },
  { name: 'Repository two', branch: 'Branch two', prs: 'Pull request two', workspace: 'Workspace two', lastCommit: 'Timestamp two' },
  { name: 'Repository three', branch: 'Branch three', prs: 'Pull request three', workspace: 'Workspace three', lastCommit: 'Timestamp three' },
  { name: 'Repository four', branch: 'Branch four', prs: 'Pull request four', workspace: 'Workspace four', lastCommit: 'Timestamp four' },
  { name: 'Repository five', branch: 'Branch five', prs: 'Pull request five', workspace: 'Workspace five', lastCommit: 'Timestamp five' },
  { name: 'Repository six', branch: 'Branch six', prs: 'Pull request six', workspace: 'Workspace six', lastCommit: 'Timestamp six' }
];

const filterOptions: DataViewFilterOption[] = [
  { label: 'Workspace one', value: 'workspace-one' },
  { label: 'Workspace two', value: 'workspace-two' },
  { label: 'Workspace three', value: 'workspace-three' }
];

const COLUMNS = [
  { label: 'Repository', key: 'name', index: 0 },
  { label: 'Branch', key: 'branches', index: 1 },
  { label: 'Pull request', key: 'prs', index: 2 },
  { label: 'Workspace', key: 'workspaces', index: 3 },
  { label: 'Last commit', key: 'lastCommit', index: 4 }
];

const ouiaId = 'LayoutExample';

const sortData = (data: Repository[], sortBy: string | undefined, direction: 'asc' | 'desc' | undefined) =>
  sortBy && direction
    ? [ ...data ].sort((a, b) =>
      direction === 'asc'
        ? a[sortBy] < b[sortBy] ? -1 : a[sortBy] > b[sortBy] ? 1 : 0
        : a[sortBy] > b[sortBy] ? -1 : a[sortBy] < b[sortBy] ? 1 : 0
    )
    : data;

const empty = (
  <Tbody>
    <Tr key="loading" ouiaId={`${ouiaId}-tr-loading`}>
      <Td colSpan={COLUMNS.length}>
        <EmptyState headingLevel="h4" icon={CubesIcon} titleText="No data found">
          <EmptyStateBody>There are no matching data to be displayed.</EmptyStateBody>
          <EmptyStateFooter>
            <EmptyStateActions>
              <Button variant="primary">Primary action</Button>
            </EmptyStateActions>
            <EmptyStateActions>
              <Button variant="link">Multiple</Button>
              <Button variant="link">Action Buttons</Button>
            </EmptyStateActions>
          </EmptyStateFooter>
        </EmptyState>
      </Td>
    </Tr>
  </Tbody>
);

interface RepositoryDetailProps {
  selectedRepo?: Repository;
  setSelectedRepo: React.Dispatch<React.SetStateAction<Repository | undefined>>;
}

const RepositoryDetail: FunctionComponent<RepositoryDetailProps> = ({ selectedRepo, setSelectedRepo }) => {
  const context = useDataViewEventsContext();

  useEffect(() => {
    const unsubscribe = context.subscribe(EventTypes.rowClick, (repo: Repository) => {
      setSelectedRepo(repo);
    });

    return () => unsubscribe();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <DrawerPanelContent>
      <DrawerHead>
        <Title className="pf-v5-u-mb-md" headingLevel="h2" ouiaId="detail-drawer-title">
          Detail of {selectedRepo?.name}
        </Title>
        <Content component="p">Branch: {selectedRepo?.branch}</Content>
        <Content component="p">Pull requests: {selectedRepo?.prs}</Content>
        <Content component="p">Workspace: {selectedRepo?.workspace}</Content>
        <Content component="p">Last commit: {selectedRepo?.lastCommit}</Content>
        <DrawerActions>
          <DrawerCloseButton onClick={() => setSelectedRepo(undefined)} data-ouia-component-id="detail-drawer-close-btn"/>
        </DrawerActions>
      </DrawerHead>
    </DrawerPanelContent>
  );
};

interface RepositoriesTableProps {
  selectedRepo?: Repository;
}

const rowActions = [
  {
    title: 'Some action',
    onClick: () => console.log('clicked on Some action')  // eslint-disable-line no-console
  },
  {
    title: <div>Another action</div>,
    onClick: () => console.log('clicked on Another action')  // eslint-disable-line no-console
  },
  {
    isSeparator: true
  },
  {
    title: 'Third action',
    onClick: () => console.log('clicked on Third action')  // eslint-disable-line no-console
  }
];

const RepositoriesTable: FunctionComponent<RepositoriesTableProps> = ({ selectedRepo = undefined }) => {
  const { filters, onSetFilters, clearAllFilters } = useDataViewFilters<RepositoryFilters>({ initialFilters: { name: '', branch: '', workspace: [] } });
  
  const pagination = useDataViewPagination({ perPage: 5 });
  const { page, perPage } = pagination;

  const selection = useDataViewSelection({ matchOption: (a, b) => a.row[0] === b.row[0] });
  const { selected, onSelect, isSelected } = selection;
  
  const { trigger } = useDataViewEventsContext();

  const { sortBy, direction, onSort } = useDataViewSort();
  const sortByIndex = useMemo(() => COLUMNS.findIndex(item => item.key === sortBy), [ sortBy ]);
  const getSortParams = (columnIndex: number): ThProps['sort'] => ({
    sortBy: {
      index: sortByIndex,
      direction,
      defaultDirection: 'asc'
    },
    onSort: (_event, index, direction) => onSort(_event, COLUMNS[index].key, direction),
    columnIndex
  });

  const columns: DataViewTh[] = COLUMNS.map((column, index) => ({
    cell: column.label,
    props: { sort: getSortParams(index) }
  }));

  const finalData = useMemo(() => sortData(repositories, sortBy, direction).filter(item => 
    (!filters.name || item.name?.toLocaleLowerCase().includes(filters.name?.toLocaleLowerCase())) &&
    (!filters.branch || item.branch?.toLocaleLowerCase().includes(filters.branch?.toLocaleLowerCase())) &&
    (!filters.workspace || filters.workspace.length === 0 || filters.workspace.includes(String(filterOptions.find(option => option.label === item.workspace)?.value)))
  ), [ filters, sortBy, direction ]);

  const pageRows = useMemo(() => {
    const handleRowClick = (event, repo: Repository | undefined) => {
      // prevents drawer toggle on actions or checkbox click
      (event.target.matches('td') || event.target.matches('tr')) && trigger(EventTypes.rowClick, repo);
    };

    return finalData.map(repo => ({
      row: [ ...Object.values(repo), { cell: <ActionsColumn items={rowActions}/>, props: { isActionCell: true } } ],
      props: {
        isClickable: true,
        onRowClick: (event) => handleRowClick(event, selectedRepo?.name === repo.name ? undefined : repo),
        isRowSelected: selectedRepo?.name === repo.name
      }
    })).slice((page - 1) * perPage, ((page - 1) * perPage) + perPage);
  }, [ selectedRepo?.name, trigger, page, perPage, finalData ]);

  const handleBulkSelect = (value: BulkSelectValue) => {
    value === BulkSelectValue.none && onSelect(false);
    value === BulkSelectValue.nonePage && onSelect(false, pageRows);
    value === BulkSelectValue.page && onSelect(true, pageRows);
  };

  return (
    <DataView selection={selection} activeState={finalData.length > 0 ? undefined : 'empty'}>
      <DataViewToolbar 
        ouiaId='LayoutExampleHeader'
        clearAllFilters={clearAllFilters}
        bulkSelect={
          <BulkSelect
            pageCount={pageRows.length}
            totalCount={repositories.length}
            selectedCount={selected.length}
            pageSelected={pageRows.every(item => isSelected(item))}
            pagePartiallySelected={pageRows.some(item => isSelected(item)) && !pageRows.every(item => isSelected(item))}
            onSelect={handleBulkSelect}
          />
        }
        filters={ 
          <DataViewFilters onChange={(_e, values) => onSetFilters(values)} values={filters}>
            <DataViewTextFilter filterId="name" title='Name' placeholder='Filter by name' />
            <DataViewTextFilter filterId="branch" title='Branch' placeholder='Filter by branch' />
            <DataViewCheckboxFilter filterId="workspace" title='Workspace' placeholder='Filter by workspace' options={filterOptions} />
          </DataViewFilters>
        }
        actions={
          <ResponsiveActions ouiaId="example-actions">
            <ResponsiveAction>Add repository</ResponsiveAction>
            <ResponsiveAction>Delete repository</ResponsiveAction>
          </ResponsiveActions>
        }
        pagination={
          <Pagination
            isCompact
            perPageOptions={perPageOptions} 
            itemCount={repositories.length} 
            {...pagination} 
          />
        } 
      />
      <DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={pageRows} bodyStates={{ empty }} />
      <DataViewToolbar
        ouiaId='LayoutExampleFooter'
        pagination={
          <Pagination 
            isCompact  
            perPageOptions={perPageOptions} 
            itemCount={repositories.length} 
            {...pagination} 
          />
        } 
      />
    </DataView>
  );
};

export const BasicExample: FunctionComponent = () => {
  const [ selectedRepo, setSelectedRepo ] = useState<Repository>();
  const drawerRef = useRef<HTMLDivElement>(null);

  return (
    <DataViewEventsProvider>
      <Drawer isExpanded={Boolean(selectedRepo)} onExpand={() => drawerRef.current?.focus()} data-ouia-component-id="detail-drawer" >
        <DrawerContent
          panelContent={<RepositoryDetail selectedRepo={selectedRepo} setSelectedRepo={setSelectedRepo} />}
        >
          <DrawerContentBody>
            <RepositoriesTable selectedRepo={selectedRepo} />
          </DrawerContentBody>
        </DrawerContent>
      </Drawer>
    </DataViewEventsProvider>
  );
};

```