Skip to content

Commit

Permalink
DataViews: Full type the dataviews package (#61854)
Browse files Browse the repository at this point in the history
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
  • Loading branch information
3 people authored May 27, 2024
1 parent 2b94b3b commit 5498ae2
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 64 deletions.
4 changes: 2 additions & 2 deletions packages/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,11 @@ Helper hook for input fields that need to debounce the value before using it.

_Parameters_

- _defaultValue_ `any`: The default value to use.
- _defaultValue_ The default value to use.

_Returns_

- `[string, Function, string]`: The input value, the setter and the debounced input value.
- `[ string, ( value: string ) => void, string ]`: The input value, the setter and the debounced input value.

### useDisabled

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ import useDebounce from '../use-debounce';
/**
* Helper hook for input fields that need to debounce the value before using it.
*
* @param {any} defaultValue The default value to use.
* @return {[string, Function, string]} The input value, the setter and the debounced input value.
* @param defaultValue The default value to use.
* @return The input value, the setter and the debounced input value.
*/
export default function useDebouncedInput( defaultValue = '' ) {
const [ input, setInput ] = useState( defaultValue );
export default function useDebouncedInput(
defaultValue = ''
): [ string, ( value: string ) => void, string ] {
const [ input, setInput ] = useState< string >( defaultValue );
const [ debouncedInput, setDebouncedState ] = useState( defaultValue );

const setDebouncedInput = useDebounce( setDebouncedState, 250 );

useEffect( () => {
setDebouncedInput( input );
}, [ input ] );
}, [ input, setDebouncedInput ] );

return [ input, setInput, debouncedInput ];
}
18 changes: 9 additions & 9 deletions packages/dataviews/src/bulk-actions-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ interface ToolbarContentProps< Item extends AnyItem > {
selection: string[];
actionsToShow: Action< Item >[];
selectedItems: Item[];
setSelection: ( selection: Item[] ) => void;
onSelectionChange: ( selection: Item[] ) => void;
}

interface BulkActionsToolbarProps< Item extends AnyItem > {
data: Item[];
selection: string[];
actions: Action< Item >[];
setSelection: ( selection: Item[] ) => void;
onSelectionChange: ( selection: Item[] ) => void;
getItemId: ( item: Item ) => string;
}

Expand Down Expand Up @@ -123,7 +123,7 @@ function renderToolbarContent< Item extends AnyItem >(
selectedItems: Item[],
actionInProgress: string | null,
setActionInProgress: ( actionId: string | null ) => void,
setSelection: ( selection: Item[] ) => void
onSelectionChange: ( selection: Item[] ) => void
) {
return (
<>
Expand Down Expand Up @@ -163,7 +163,7 @@ function renderToolbarContent< Item extends AnyItem >(
label={ __( 'Cancel' ) }
disabled={ !! actionInProgress }
onClick={ () => {
setSelection( EMPTY_ARRAY );
onSelectionChange( EMPTY_ARRAY );
} }
/>
</ToolbarGroup>
Expand All @@ -175,7 +175,7 @@ function ToolbarContent< Item extends AnyItem >( {
selection,
actionsToShow,
selectedItems,
setSelection,
onSelectionChange,
}: ToolbarContentProps< Item > ) {
const [ actionInProgress, setActionInProgress ] = useState< string | null >(
null
Expand All @@ -191,7 +191,7 @@ function ToolbarContent< Item extends AnyItem >( {
selectedItems,
actionInProgress,
setActionInProgress,
setSelection
onSelectionChange
);
} else if ( ! buttons.current ) {
buttons.current = renderToolbarContent(
Expand All @@ -200,7 +200,7 @@ function ToolbarContent< Item extends AnyItem >( {
selectedItems,
actionInProgress,
setActionInProgress,
setSelection
onSelectionChange
);
}
return buttons.current;
Expand All @@ -210,7 +210,7 @@ export default function BulkActionsToolbar< Item extends AnyItem >( {
data,
selection,
actions = EMPTY_ARRAY,
setSelection,
onSelectionChange,
getItemId,
}: BulkActionsToolbarProps< Item > ) {
const isReducedMotion = useReducedMotion();
Expand Down Expand Up @@ -258,7 +258,7 @@ export default function BulkActionsToolbar< Item extends AnyItem >( {
selection={ selection }
actionsToShow={ actionsToShow }
selectedItems={ selectedItems }
setSelection={ setSelection }
onSelectionChange={ onSelectionChange }
/>
</div>
</Toolbar>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import type { ComponentType } from 'react';

/**
* WordPress dependencies
*/
Expand All @@ -16,21 +21,46 @@ import { VIEW_LAYOUTS } from './layouts';
import BulkActions from './bulk-actions';
import { normalizeFields } from './normalize-fields';
import BulkActionsToolbar from './bulk-actions-toolbar';
import type { Action, AnyItem, Field, View, ViewBaseProps } from './types';

interface DataViewsProps< Item extends AnyItem > {
view: View;
onChangeView: ( view: View ) => void;
fields: Field< Item >[];
search?: boolean;
searchLabel?: string;
actions?: Action< Item >[];
data: Item[];
getItemId?: ( item: Item ) => string;
isLoading?: boolean;
paginationInfo: {
totalItems: number;
totalPages: number;
};
supportedLayouts: string[];
onSelectionChange?: ( items: Item[] ) => void;
}

const defaultGetItemId = ( item ) => item.id;
const defaultGetItemId = ( item: AnyItem ) => item.id;
const defaultOnSelectionChange = () => {};

function useSomeItemHasAPossibleBulkAction( actions, data ) {
function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >(
actions: Action< Item >[],
data: Item[]
) {
return useMemo( () => {
return data.some( ( item ) => {
return actions.some( ( action ) => {
return action.supportsBulk && action.isEligible( item );
return (
action.supportsBulk &&
( ! action.isEligible || action.isEligible( item ) )
);
} );
} );
}, [ actions, data ] );
}

export default function DataViews( {
export default function DataViews< Item extends AnyItem >( {
view,
onChangeView,
fields,
Expand All @@ -43,9 +73,9 @@ export default function DataViews( {
paginationInfo,
supportedLayouts,
onSelectionChange = defaultOnSelectionChange,
} ) {
const [ selection, setSelection ] = useState( [] );
const [ openedFilter, setOpenedFilter ] = useState( null );
}: DataViewsProps< Item > ) {
const [ selection, setSelection ] = useState< string[] >( [] );
const [ openedFilter, setOpenedFilter ] = useState< string | null >( null );

useEffect( () => {
if (
Expand All @@ -67,16 +97,15 @@ export default function DataViews( {
}, [ selection, data, getItemId, onSelectionChange ] );

const onSetSelection = useCallback(
( items ) => {
( items: Item[] ) => {
setSelection( items.map( ( item ) => getItemId( item ) ) );
onSelectionChange( items );
},
[ setSelection, getItemId, onSelectionChange ]
);

const ViewComponent = VIEW_LAYOUTS.find(
( v ) => v.type === view.type
).component;
const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type )
?.component as ComponentType< ViewBaseProps< Item > >;
const _fields = useMemo( () => normalizeFields( fields ), [ fields ] );

const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction(
Expand Down Expand Up @@ -150,7 +179,7 @@ export default function DataViews( {
data={ data }
actions={ actions }
selection={ selection }
setSelection={ setSelection }
onSelectionChange={ onSetSelection }
getItemId={ getItemId }
/>
) }
Expand Down
7 changes: 5 additions & 2 deletions packages/dataviews/src/filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface FiltersProps< Item extends AnyItem > {
setOpenedFilter: ( openedFilter: string | null ) => void;
}

const Filters = memo( function Filters< Item extends AnyItem >( {
function _Filters< Item extends AnyItem >( {
fields,
view,
onChangeView,
Expand Down Expand Up @@ -117,6 +117,9 @@ const Filters = memo( function Filters< Item extends AnyItem >( {
{ filterComponents }
</HStack>
);
} );
}

// A type assertion is used here to keep the type argument.
const Filters = memo( _Filters ) as typeof _Filters;

export default Filters;
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,37 @@ import { useEffect, useRef, memo } from '@wordpress/element';
import { SearchControl } from '@wordpress/components';
import { useDebouncedInput } from '@wordpress/compose';

const Search = memo( function Search( { label, view, onChangeView } ) {
/**
* Internal dependencies
*/
import type { View } from './types';

interface SearchProps {
label?: string;
view: View;
onChangeView: ( view: View ) => void;
}

const Search = memo( function Search( {
label,
view,
onChangeView,
}: SearchProps ) {
const [ search, setSearch, debouncedSearch ] = useDebouncedInput(
view.search
);
useEffect( () => {
setSearch( view.search );
}, [ view ] );
setSearch( view.search ?? '' );
}, [ view.search, setSearch ] );
const onChangeViewRef = useRef( onChangeView );
const viewRef = useRef( view );
useEffect( () => {
onChangeViewRef.current = onChangeView;
}, [ onChangeView ] );
viewRef.current = view;
}, [ onChangeView, view ] );
useEffect( () => {
onChangeViewRef.current( {
...view,
...viewRef.current,
page: 1,
search: debouncedSearch,
} );
Expand Down
26 changes: 23 additions & 3 deletions packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,15 +373,35 @@ export type Action< Item extends AnyItem > =
| ActionModal< Item >
| ActionButton< Item >;

export interface ViewProps< Item extends AnyItem, ViewType extends ViewBase > {
export interface ViewBaseProps< Item extends AnyItem > {
actions: Action< Item >[];
data: Item[];
fields: NormalizedField< Item >[];
getItemId: ( item: Item ) => string;
isLoading?: boolean;
onChangeView( view: ViewType ): void;
onChangeView( view: View ): void;
onSelectionChange: ( items: Item[] ) => void;
selection: string[];
setOpenedFilter: ( fieldId: string ) => void;
view: ViewType;
view: View;
}

export interface ViewTableProps< Item extends AnyItem >
extends ViewBaseProps< Item > {
view: ViewTable;
}

export interface ViewListProps< Item extends AnyItem >
extends ViewBaseProps< Item > {
view: ViewList;
}

export interface ViewGridProps< Item extends AnyItem >
extends ViewBaseProps< Item > {
view: ViewGrid;
}

export type ViewProps< Item extends AnyItem > =
| ViewTableProps< Item >
| ViewGridProps< Item >
| ViewListProps< Item >;
7 changes: 5 additions & 2 deletions packages/dataviews/src/view-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function SortMenu< Item extends AnyItem >( {
);
}

const ViewActions = memo( function ViewActions< Item extends AnyItem >( {
function _ViewActions< Item extends AnyItem >( {
fields,
view,
onChangeView,
Expand Down Expand Up @@ -339,6 +339,9 @@ const ViewActions = memo( function ViewActions< Item extends AnyItem >( {
</DropdownMenuGroup>
</DropdownMenu>
);
} );
}

// A type assertion is used here to keep the type argument.
const ViewActions = memo( _ViewActions ) as typeof _ViewActions;

export default ViewActions;
11 changes: 1 addition & 10 deletions packages/dataviews/src/view-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,7 @@ import { __ } from '@wordpress/i18n';
import ItemActions from './item-actions';
import SingleSelectionCheckbox from './single-selection-checkbox';
import { useHasAPossibleBulkAction } from './bulk-actions';
import type {
Action,
AnyItem,
NormalizedField,
ViewGrid as ViewGridType,
ViewProps,
} from './types';

interface ViewGridProps< Item extends AnyItem >
extends ViewProps< Item, ViewGridType > {}
import type { Action, AnyItem, NormalizedField, ViewGridProps } from './types';

interface GridItemProps< Item extends AnyItem > {
selection: string[];
Expand Down
11 changes: 1 addition & 10 deletions packages/dataviews/src/view-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,10 @@ import { moreVertical } from '@wordpress/icons';
* Internal dependencies
*/
import { unlock } from './lock-unlock';
import type {
Action,
AnyItem,
NormalizedField,
ViewList as ViewListType,
ViewProps,
} from './types';
import type { Action, AnyItem, NormalizedField, ViewListProps } from './types';

import { ActionsDropdownMenuGroup, ActionModal } from './item-actions';

interface ViewListProps< Item extends AnyItem >
extends ViewProps< Item, ViewListType > {}

interface ListViewItemProps< Item extends AnyItem > {
actions: Action< Item >[];
id?: string;
Expand Down
Loading

0 comments on commit 5498ae2

Please sign in to comment.