Skip to content

Commit

Permalink
Set up cell popover context
Browse files Browse the repository at this point in the history
- set up initial open/location state, + open/close popover APIs returned to consumers

- improve auto props documentation - remove EuiDataGridCellLocation in favor of specifying rowIndex and colIndex (it's less DRY but it's easier for devs to not have to look up EuiDataGridCellLocation from our docs)
  • Loading branch information
cee-chen committed Jan 20, 2022
1 parent a5bb3b3 commit 100e314
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 119 deletions.
41 changes: 40 additions & 1 deletion src/components/datagrid/body/data_grid_cell_popover.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,49 @@
*/

import React from 'react';
import { act } from 'react-dom/test-utils';
import { shallow } from 'enzyme';
import { keys } from '../../../services';
import { testCustomHook } from '../../../test';

import { EuiDataGridCellPopover } from './data_grid_cell_popover';
import {
useCellPopover,
EuiDataGridCellPopover,
} from './data_grid_cell_popover';

describe('useCellPopover', () => {
describe('openCellPopover', () => {
it('sets popoverIsOpen state to true', () => {
const {
return: { cellPopoverContext },
getUpdatedState,
} = testCustomHook(() => useCellPopover());
expect(cellPopoverContext.popoverIsOpen).toEqual(false);

act(() =>
cellPopoverContext.openCellPopover({ rowIndex: 0, colIndex: 0 })
);
expect(getUpdatedState().cellPopoverContext.popoverIsOpen).toEqual(true);
});
});

describe('closeCellPopover', () => {
it('sets popoverIsOpen state to false', () => {
const {
return: { cellPopoverContext },
getUpdatedState,
} = testCustomHook(() => useCellPopover());

act(() =>
cellPopoverContext.openCellPopover({ rowIndex: 0, colIndex: 0 })
);
expect(getUpdatedState().cellPopoverContext.popoverIsOpen).toEqual(true);

act(() => cellPopoverContext.closeCellPopover());
expect(getUpdatedState().cellPopoverContext.popoverIsOpen).toEqual(false);
});
});
});

describe('EuiDataGridCellPopover', () => {
const requiredProps = {
Expand Down
46 changes: 45 additions & 1 deletion src/components/datagrid/body/data_grid_cell_popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,62 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { JSXElementConstructor } from 'react';

import React, {
createContext,
useState,
useCallback,
JSXElementConstructor,
} from 'react';
import { keys } from '../../../services';
import { EuiButtonEmpty, EuiButtonEmptyProps } from '../../button/button_empty';
import { EuiFlexGroup, EuiFlexItem } from '../../flex';
import { EuiPopover, EuiPopoverFooter } from '../../popover';
import {
DataGridCellPopoverContextShape,
EuiDataGridCellPopoverProps,
EuiDataGridCellValueElementProps,
EuiDataGridColumnCellAction,
EuiDataGridColumnCellActionProps,
} from '../data_grid_types';

export const DataGridCellPopoverContext = createContext<
DataGridCellPopoverContextShape
>({
popoverIsOpen: false,
openCellLocation: { rowIndex: 0, colIndex: 0 },
openCellPopover: () => {},
closeCellPopover: () => {},
});

export const useCellPopover = (): {
cellPopoverContext: DataGridCellPopoverContextShape;
} => {
// Current open state & cell location are handled here
const [popoverIsOpen, setPopoverIsOpen] = useState(false);
const [openCellLocation, setOpenCellLocation] = useState({
rowIndex: 0,
colIndex: 0,
});

const closeCellPopover = useCallback(() => setPopoverIsOpen(false), []);
const openCellPopover = useCallback(({ rowIndex, colIndex }) => {
// TODO: Popover anchor & content

setOpenCellLocation({ rowIndex, colIndex });
setPopoverIsOpen(true);
}, []);

const cellPopoverContext = {
popoverIsOpen,
closeCellPopover,
openCellPopover,
openCellLocation,
};

return { cellPopoverContext };
};

export function EuiDataGridCellPopover({
anchorContent,
cellContentProps,
Expand Down
2 changes: 2 additions & 0 deletions src/components/datagrid/data_grid.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2762,6 +2762,8 @@ describe('EuiDataGrid', () => {
expect(gridRef.current).toEqual({
setIsFullScreen: expect.any(Function),
setFocusedCell: expect.any(Function),
openCellPopover: expect.any(Function),
closeCellPopover: expect.any(Function),
});
});
});
241 changes: 127 additions & 114 deletions src/components/datagrid/data_grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ import {
EuiDataGridInMemoryRenderer,
} from './utils/in_memory';
import { useHeaderIsInteractive } from './body/header/header_is_interactive';
import {
DataGridCellPopoverContext,
useCellPopover,
} from './body/data_grid_cell_popover';
import { providedPopoverContents } from './body/popover_utils';
import { computeVisibleRows } from './utils/row_count';
import { EuiDataGridPaginationRenderer } from './utils/data_grid_pagination';
Expand Down Expand Up @@ -273,6 +277,11 @@ export const EuiDataGrid = forwardRef<EuiDataGridRefProps, EuiDataGridProps>(
gridItemsRendered,
});

/**
* Cell popover
*/
const { cellPopoverContext } = useCellPopover();

/**
* Toolbar & full-screen
*/
Expand Down Expand Up @@ -302,8 +311,10 @@ export const EuiDataGrid = forwardRef<EuiDataGridRefProps, EuiDataGridProps>(
setFocusedCell: ({ rowIndex, colIndex }) => {
focusContext.setFocusedCell([colIndex, rowIndex]);
},
openCellPopover: cellPopoverContext.openCellPopover,
closeCellPopover: cellPopoverContext.closeCellPopover,
}),
[focusContext]
[focusContext, cellPopoverContext]
);

/**
Expand Down Expand Up @@ -381,122 +392,124 @@ export const EuiDataGrid = forwardRef<EuiDataGridRefProps, EuiDataGridProps>(

return (
<DataGridFocusContext.Provider value={focusContext}>
<DataGridSortingContext.Provider value={sortingContext}>
<EuiFocusTrap
disabled={!isFullScreen}
className="euiDataGrid__focusWrap"
>
<div
className={classes}
onKeyDown={handleGridKeyDown}
style={isFullScreen ? undefined : { width, height }}
ref={resizeRef}
{...rest}
<DataGridCellPopoverContext.Provider value={cellPopoverContext}>
<DataGridSortingContext.Provider value={sortingContext}>
<EuiFocusTrap
disabled={!isFullScreen}
className="euiDataGrid__focusWrap"
>
{showToolbar && (
<EuiDataGridToolbar
setRef={setToolbarRef}
gridWidth={gridWidth}
minSizeForControls={minSizeForControls}
toolbarVisibility={toolbarVisibility}
displaySelector={displaySelector}
isFullScreen={isFullScreen}
setIsFullScreen={setIsFullScreen}
controlBtnClasses={controlBtnClasses}
columnSelector={columnSelector}
columnSorting={columnSorting}
/>
)}
{inMemory ? (
<EuiDataGridInMemoryRenderer
inMemory={inMemory}
renderCellValue={renderCellValue}
columns={columns}
rowCount={
inMemory.level === 'enhancements'
? // if `inMemory.level === enhancements` then we can only be sure the pagination's pageSize is available in memory
pagination?.pageSize || rowCount
: // otherwise, all of the data is present and usable
rowCount
}
onCellRender={onCellRender}
/>
) : null}
<div // eslint-disable-line jsx-a11y/interactive-supports-focus
ref={contentRef}
onKeyDown={createKeyDownHandler({
gridElement: contentRef.current,
visibleColCount,
visibleRowCount,
visibleRowStartIndex:
gridItemsRendered.current?.visibleRowStartIndex || 0,
rowCount,
pagination,
hasFooter: !!renderFooterCellValue,
headerIsInteractive,
focusContext,
})}
data-test-subj="euiDataGridBody"
className="euiDataGrid__content"
role="grid"
id={gridId}
{...wrappingDivFocusProps} // re: above jsx-a11y - tabIndex is handled by these props, but the linter isn't smart enough to know that
{...gridAriaProps}
<div
className={classes}
onKeyDown={handleGridKeyDown}
style={isFullScreen ? undefined : { width, height }}
ref={resizeRef}
{...rest}
>
<EuiDataGridBody
isFullScreen={isFullScreen}
columns={orderedVisibleColumns}
visibleColCount={visibleColCount}
toolbarHeight={toolbarHeight}
leadingControlColumns={leadingControlColumns}
schema={mergedSchema}
trailingControlColumns={trailingControlColumns}
setVisibleColumns={setVisibleColumns}
switchColumnPos={switchColumnPos}
onColumnResize={onColumnResize}
headerIsInteractive={headerIsInteractive}
handleHeaderMutation={handleHeaderMutation}
schemaDetectors={allSchemaDetectors}
popoverContents={mergedPopoverContents}
pagination={pagination}
renderCellValue={renderCellValue}
renderFooterCellValue={renderFooterCellValue}
rowCount={rowCount}
visibleRows={visibleRows}
interactiveCellId={interactiveCellId}
rowHeightsOptions={rowHeightsOptions}
virtualizationOptions={virtualizationOptions || {}}
gridStyles={gridStyles}
gridWidth={gridWidth}
gridRef={gridRef}
gridItemsRendered={gridItemsRendered}
wrapperRef={contentRef}
/>
</div>
{pagination && props['aria-labelledby'] && (
<p id={ariaLabelledById} hidden>
{ariaLabelledBy}
{showToolbar && (
<EuiDataGridToolbar
setRef={setToolbarRef}
gridWidth={gridWidth}
minSizeForControls={minSizeForControls}
toolbarVisibility={toolbarVisibility}
displaySelector={displaySelector}
isFullScreen={isFullScreen}
setIsFullScreen={setIsFullScreen}
controlBtnClasses={controlBtnClasses}
columnSelector={columnSelector}
columnSorting={columnSorting}
/>
)}
{inMemory ? (
<EuiDataGridInMemoryRenderer
inMemory={inMemory}
renderCellValue={renderCellValue}
columns={columns}
rowCount={
inMemory.level === 'enhancements'
? // if `inMemory.level === enhancements` then we can only be sure the pagination's pageSize is available in memory
pagination?.pageSize || rowCount
: // otherwise, all of the data is present and usable
rowCount
}
onCellRender={onCellRender}
/>
) : null}
<div // eslint-disable-line jsx-a11y/interactive-supports-focus
ref={contentRef}
onKeyDown={createKeyDownHandler({
gridElement: contentRef.current,
visibleColCount,
visibleRowCount,
visibleRowStartIndex:
gridItemsRendered.current?.visibleRowStartIndex || 0,
rowCount,
pagination,
hasFooter: !!renderFooterCellValue,
headerIsInteractive,
focusContext,
})}
data-test-subj="euiDataGridBody"
className="euiDataGrid__content"
role="grid"
id={gridId}
{...wrappingDivFocusProps} // re: above jsx-a11y - tabIndex is handled by these props, but the linter isn't smart enough to know that
{...gridAriaProps}
>
<EuiDataGridBody
isFullScreen={isFullScreen}
columns={orderedVisibleColumns}
visibleColCount={visibleColCount}
toolbarHeight={toolbarHeight}
leadingControlColumns={leadingControlColumns}
schema={mergedSchema}
trailingControlColumns={trailingControlColumns}
setVisibleColumns={setVisibleColumns}
switchColumnPos={switchColumnPos}
onColumnResize={onColumnResize}
headerIsInteractive={headerIsInteractive}
handleHeaderMutation={handleHeaderMutation}
schemaDetectors={allSchemaDetectors}
popoverContents={mergedPopoverContents}
pagination={pagination}
renderCellValue={renderCellValue}
renderFooterCellValue={renderFooterCellValue}
rowCount={rowCount}
visibleRows={visibleRows}
interactiveCellId={interactiveCellId}
rowHeightsOptions={rowHeightsOptions}
virtualizationOptions={virtualizationOptions || {}}
gridStyles={gridStyles}
gridWidth={gridWidth}
gridRef={gridRef}
gridItemsRendered={gridItemsRendered}
wrapperRef={contentRef}
/>
</div>
{pagination && props['aria-labelledby'] && (
<p id={ariaLabelledById} hidden>
{ariaLabelledBy}
</p>
)}
{pagination && (
<EuiDataGridPaginationRenderer
{...pagination}
rowCount={rowCount}
controls={gridId}
aria-label={props['aria-label']}
gridRef={gridRef}
/>
)}
<p id={interactiveCellId} hidden>
<EuiI18n
token="euiDataGrid.screenReaderNotice"
default="Cell contains interactive content."
/>
{/* TODO: if no keyboard shortcuts panel gets built, add keyboard shortcut info here */}
</p>
)}
{pagination && (
<EuiDataGridPaginationRenderer
{...pagination}
rowCount={rowCount}
controls={gridId}
aria-label={props['aria-label']}
gridRef={gridRef}
/>
)}
<p id={interactiveCellId} hidden>
<EuiI18n
token="euiDataGrid.screenReaderNotice"
default="Cell contains interactive content."
/>
{/* TODO: if no keyboard shortcuts panel gets built, add keyboard shortcut info here */}
</p>
</div>
</EuiFocusTrap>
</DataGridSortingContext.Provider>
</div>
</EuiFocusTrap>
</DataGridSortingContext.Provider>
</DataGridCellPopoverContext.Provider>
</DataGridFocusContext.Provider>
);
}
Expand Down
Loading

0 comments on commit 100e314

Please sign in to comment.