diff --git a/packages/module/patternfly-docs/content/examples/FilterableWithWindowScroller.tsx b/packages/module/patternfly-docs/content/examples/FilterableWithWindowScroller.tsx index 94af307..97a029e 100644 --- a/packages/module/patternfly-docs/content/examples/FilterableWithWindowScroller.tsx +++ b/packages/module/patternfly-docs/content/examples/FilterableWithWindowScroller.tsx @@ -28,9 +28,9 @@ import { import { FilterIcon, SearchIcon } from '@patternfly/react-icons'; export const ComposableTableWindowScroller = () => { - const [scrollableElement, setScrollableElement] = React.useState(); + const [scrollableElement, setScrollableElement] = React.useState(); React.useEffect(() => { - const scrollableElement = document.getElementById('content-scrollable-2'); + const scrollableElement = document.getElementById('content-scrollable-2') as HTMLElement; setScrollableElement(scrollableElement); }, []); @@ -131,18 +131,19 @@ export const ComposableTableWindowScroller = () => { setInputValue(newValue); }; - const onStatusSelect = (event: React.MouseEvent, selection: string) => { - const checked = (event.target as HTMLInputElement).checked; + const onStatusSelect = (event: React.MouseEvent | undefined, selection: string | number | undefined) => { + const checked = (event?.target as HTMLInputElement).checked; setFilters({ ...filters, - status: checked ? [...filters.status, selection] : filters.status.filter((value) => value !== selection) + status: (checked && selection) ? [...filters.status, `${selection}`] : filters.status.filter((value) => value !== selection) }); setIsFilterDropdownOpen(false); }; - const onNameInput = (event: React.KeyboardEvent) => { + const onNameInput = (event: React.SyntheticEvent | React.KeyboardEvent) => { setIsCategoryDropdownOpen(false); - if (event.key && event.key !== 'Enter') { + const pressedKey = (event as React.KeyboardEvent).key; + if (pressedKey && pressedKey !== 'Enter') { return; } @@ -155,8 +156,8 @@ export const ComposableTableWindowScroller = () => { setIsCategoryDropdownOpen(false); }; - const onLocationSelect = (event: React.MouseEvent, selection: string) => { - setFilters({ ...filters, location: [selection] }); + const onLocationSelect = (_event: React.MouseEvent | undefined, selection: string | number | undefined) => { + setFilters({ ...filters, location: [`${selection}`] }); setIsFilterDropdownOpen(false); onFilterSelect(); @@ -293,7 +294,7 @@ export const ComposableTableWindowScroller = () => { onClear={() => { onInputChange(''); }} - onSearch={onNameInput} + onSearch={onNameInput} // any typing is needed because of what I think is a bug in the SearchInput typing /> { ); - const scrollableContainerStyle = { + interface ScrollableContainerStyle { + height: number; + overflowX: 'auto'; + overflowY: 'scroll'; + scrollBehavior: 'smooth'; + WebkitOverflowScrolling: 'touch'; + position: 'relative'; + } + + const scrollableContainerStyle: ScrollableContainerStyle = { height: 500 /* important note: the scrollable container should have some sort of fixed height, or it should be wrapped in container that is smaller than ReactVirtualized__VirtualGrid container and has overflow visible if using the Window Scroller. See WindowScroller.example.css */, overflowX: 'auto', overflowY: 'scroll', @@ -445,7 +455,7 @@ export const ComposableTableWindowScroller = () => { {({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => ( {({ width }) => ( -
+
void}> rowIndex\n });\n\n const rowRenderer = ({ index, _isScrolling, key, style, parent }) => {\n const { rows, columns } = this.state;\n\n return (\n \n \n \n {rows[index].cells[0]}\n \n \n {rows[index].cells[1]}\n \n \n {rows[index].cells[2]}\n \n \n {rows[index].cells[3]}\n \n \n {rows[index].cells[4]}\n \n \n \n );\n };\n\n return (\n
\n \n \n \n \n {({ width }) => (\n \n )}\n \n
\n );\n }\n}\n","title":"Basic","lang":"js"}}> @@ -222,7 +224,7 @@ pageData.examples = {
, 'Filterable with WindowScroller': props => - console.log('clicked on Some action, on row: ', rowId)\n },\n {\n title:
Another action
,\n onClick: (_event, rowId, _rowData, _extra) => console.log('clicked on Another action, on row: ', rowId)\n },\n {\n isSeparator: true\n },\n {\n title: 'Third action',\n onClick: (_event, rowId, _rowData, _extra) => console.log('clicked on Third action, on row: ', rowId)\n }\n ]\n };\n\n this._handleResize = debounce(this._handleResize.bind(this), 100);\n\n this.onDelete = (type = '', id = '') => {\n if (type) {\n this.setState((prevState) => {\n prevState.filters[type.toLowerCase()] = prevState.filters[type.toLowerCase()].filter((s) => s !== id);\n return {\n filters: prevState.filters\n };\n });\n } else {\n this.setState({\n filters: {\n location: [],\n name: [],\n status: []\n },\n inputValue: ''\n });\n }\n };\n\n this.onCategoryToggle = (_event, isOpen) => {\n this.setState({\n isCategoryDropdownOpen: isOpen\n });\n };\n\n this.onCategorySelect = (event) => {\n this.setState({\n currentCategory: event.target.innerText,\n isCategoryDropdownOpen: !this.state.isCategoryDropdownOpen\n });\n };\n\n this.onFilterToggle = (_event, isOpen) => {\n this.setState({\n isFilterDropdownOpen: isOpen\n });\n };\n\n this.onFilterSelect = (_event) => {\n this.setState({\n isFilterDropdownOpen: !this.state.isFilterDropdownOpen\n });\n };\n\n this.onInputChange = (_event, newValue) => {\n // this.setState({ inputValue: newValue });\n if (newValue === '') {\n this.onDelete();\n this.setState({\n inputValue: newValue\n });\n } else {\n this.setState((prevState) => ({\n filters: {\n ...prevState.filters,\n ['name']: [newValue]\n },\n inputValue: newValue\n }));\n }\n };\n\n this.onRowSelect = (event, isSelected, rowId) => {\n let rows;\n if (rowId === -1) {\n rows = this.state.rows.map((oneRow) => {\n oneRow.selected = isSelected;\n return oneRow;\n });\n } else {\n rows = [...this.state.rows];\n rows[rowId].selected = isSelected;\n }\n this.setState({\n rows\n });\n };\n\n this.onStatusSelect = (event, selection) => {\n const checked = event.target.checked;\n this.setState((prevState) => {\n const prevSelections = prevState.filters.status;\n return {\n filters: {\n ...prevState.filters,\n status: checked ? [...prevSelections, selection] : prevSelections.filter((value) => value !== selection)\n }\n };\n });\n };\n\n this.onNameInput = (event) => {\n if (event.key && event.key !== 'Enter') {\n return;\n }\n\n const { inputValue } = this.state;\n this.setState((prevState) => {\n const prevFilters = prevState.filters.name;\n return {\n filters: {\n ...prevState.filters,\n ['name']: prevFilters.includes(inputValue) ? prevFilters : [...prevFilters, inputValue]\n },\n inputValue: ''\n };\n });\n };\n\n this.onLocationSelect = (event, selection) => {\n this.setState((prevState) => ({\n filters: {\n ...prevState.filters,\n ['location']: [selection]\n }\n }));\n this.onFilterSelect();\n };\n\n this._handleResize = debounce(this._handleResize.bind(this), 100);\n this._bindBodyRef = this._bindBodyRef.bind(this);\n }\n\n componentDidMount() {\n // re-render after resize\n window.addEventListener('resize', this._handleResize);\n\n setTimeout(() => {\n const scollableElement = document.getElementById('content-scrollable-1');\n this.setState({ scollableElement });\n });\n\n // re-render after resize\n window.addEventListener('resize', this._handleResize);\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this._handleResize);\n }\n\n _handleResize() {\n this._cellMeasurementCache.clearAll();\n this._bodyRef.recomputeVirtualGridSize();\n }\n\n _bindBodyRef(ref) {\n this._bodyRef = ref;\n }\n\n buildCategoryDropdown() {\n const { isCategoryDropdownOpen, currentCategory } = this.state;\n\n return (\n \n \n {currentCategory}\n \n }\n isOpen={isCategoryDropdownOpen}\n dropdownItems={[\n Location,\n Name,\n Status\n ]}\n style={{ width: '100%' }}\n >\n \n );\n }\n\n buildFilterDropdown() {\n const { currentCategory, isFilterDropdownOpen, inputValue, filters } = this.state;\n\n const locationMenuItems = [\n ,\n ,\n ,\n ,\n \n ];\n\n const statusMenuItems = [\n ,\n ,\n ,\n ,\n \n ];\n\n return (\n \n \n \n {locationMenuItems}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {statusMenuItems}\n \n \n \n );\n }\n\n renderToolbar() {\n return (\n \n \n } breakpoint=\"xl\">\n \n {this.buildCategoryDropdown()}\n {this.buildFilterDropdown()}\n \n \n \n \n );\n }\n\n render() {\n const { loading, rows, columns, actions, filters, scollableElement } = this.state;\n\n const filteredRows =\n filters.name.length > 0 || filters.location.length > 0 || filters.status.length > 0\n ? rows.filter((row) => (\n (filters.name.length === 0 ||\n filters.name.some((name) => row.cells[0].toLowerCase().includes(name.toLowerCase()))) &&\n (filters.location.length === 0 || filters.location.includes(row.cells[4])) &&\n (filters.status.length === 0 || filters.status.includes(row.cells[3]))\n ))\n : rows;\n const measurementCache = new CellMeasurerCache({\n fixedWidth: true,\n minHeight: 44,\n keyMapper: (rowIndex) => rowIndex\n });\n\n const rowRenderer = ({ index, _isScrolling, key, style, parent }) => {\n const { actions } = this.state;\n\n return (\n \n \n {filteredRows[index].cells[0]}\n {filteredRows[index].cells[1]}\n {filteredRows[index].cells[2]}\n {filteredRows[index].cells[3]}\n {filteredRows[index].cells[4]}\n \n \n \n \n \n );\n };\n\n return (\n \n {this.renderToolbar()}\n\n \n
\n {!loading && filteredRows.length > 0 && (\n
\n \n \n \n \n {({ height, _isScrolling, registerChild, _onChildScroll, scrollTop }) => (\n \n {({ width }) => (\n
\n (this.actionsVirtualBody = ref)}\n autoHeight\n className=\"pf-v5-c-table pf-v5-c-virtualized pf-v5-c-window-scroller\"\n deferredMeasurementCache={measurementCache}\n rowHeight={measurementCache.rowHeight}\n height={height || 0}\n overscanRowCount={10}\n columnCount={6}\n rows={filteredRows}\n rowCount={filteredRows.length}\n rowRenderer={rowRenderer}\n scrollTop={scrollTop}\n width={width}\n role=\"grid\"\n />\n
\n )}\n
\n )}\n
\n
\n )}\n
\n
\n \n );\n }\n}\n","title":"Filterable with WindowScroller","lang":"js"}}> + {\n const [scrollableElement, setScrollableElement] = React.useState();\n React.useEffect(() => {\n const scrollableElement = document.getElementById('content-scrollable-2') as HTMLElement;\n setScrollableElement(scrollableElement);\n }, []);\n\n interface DataType {\n cells: (string | number)[];\n id: string;\n disableActions: boolean;\n }\n\n const rows: DataType[] = [];\n for (let i = 0; i < 100; i++) {\n if (i % 2 === 0) {\n rows.push({\n disableActions: false,\n id: `actions-row-${i}`,\n cells: [`US-Node ${i}`, i, i, 'Down', 'Brno']\n });\n } else if (i % 3 === 0) {\n rows.push({\n disableActions: false,\n id: `actions-row-${i}`,\n cells: [`CN-Node ${i}`, i, i, 'Running', 'Westford']\n });\n } else {\n rows.push({\n disableActions: true,\n id: `actions-row-${i}`,\n cells: [`US-Node ${i}`, i, i, 'Stopped', 'Raleigh']\n });\n }\n }\n\n const actions = [\n {\n title: 'Some action',\n // eslint-disable-next-line no-console\n onClick: (_event, rowId, _rowData, _extra) => console.log('clicked on Some action, on row: ', rowId)\n },\n {\n title:
Another action
,\n // eslint-disable-next-line no-console\n onClick: (_event, rowId, _rowData, _extra) => console.log('clicked on Another action, on row: ', rowId)\n },\n {\n isSeparator: true\n },\n {\n title: 'Third action',\n // eslint-disable-next-line no-console\n onClick: (_event, rowId, _rowData, _extra) => console.log('clicked on Third action, on row: ', rowId)\n }\n ];\n\n const columns = ['Servers', 'Threads', 'Applications', 'Status', 'Location'];\n const scrollToIndex = -1; // can be used to programmatically set current index\n\n const [isCategoryDropdownOpen, setIsCategoryDropdownOpen] = React.useState(false);\n const [isFilterDropdownOpen, setIsFilterDropdownOpen] = React.useState(false);\n const [currentCategory, setCurrentCategory] = React.useState('Name');\n const [filters, setFilters] = React.useState>({ location: [], name: [], status: [] });\n const [inputValue, setInputValue] = React.useState('');\n\n const onDelete = (type: string | ToolbarChipGroup, id: string) => {\n if (type === 'Location') {\n setFilters({\n ...filters,\n location: filters.location.filter((fil: string) => fil !== id)\n });\n } else if (type === 'Name') {\n setFilters({\n ...filters,\n name: filters.name.filter((fil: string) => fil !== id)\n });\n } else if (type === 'Status') {\n setFilters({\n ...filters,\n status: filters.status.filter((fil: string) => fil !== id)\n });\n } else {\n setFilters({ location: [], name: [], status: [] });\n }\n };\n\n const onCategoryToggle = () => {\n setIsCategoryDropdownOpen(!isCategoryDropdownOpen);\n };\n\n const onCategorySelect = (event) => {\n setCurrentCategory(event.target.innerText);\n setIsCategoryDropdownOpen(!isCategoryDropdownOpen);\n };\n\n const onFilterToggle = () => {\n setIsFilterDropdownOpen(!isFilterDropdownOpen);\n };\n\n const onInputChange = (newValue: string) => {\n setInputValue(newValue);\n };\n\n const onStatusSelect = (event: React.MouseEvent | undefined, selection: string | number | undefined) => {\n const checked = (event?.target as HTMLInputElement).checked;\n setFilters({\n ...filters,\n status: (checked && selection) ? [...filters.status, `${selection}`] : filters.status.filter((value) => value !== selection)\n });\n setIsFilterDropdownOpen(false);\n };\n\n const onNameInput = (event: React.SyntheticEvent | React.KeyboardEvent) => {\n setIsCategoryDropdownOpen(false);\n const pressedKey = (event as React.KeyboardEvent).key;\n if (pressedKey && pressedKey !== 'Enter') {\n return;\n }\n\n const prevFilters = filters.name;\n setFilters({ ...filters, name: prevFilters.includes(inputValue) ? prevFilters : [...prevFilters, inputValue] });\n };\n\n const onFilterSelect = () => {\n setIsFilterDropdownOpen(!isFilterDropdownOpen);\n setIsCategoryDropdownOpen(false);\n };\n\n const onLocationSelect = (_event: React.MouseEvent | undefined, selection: string | number | undefined) => {\n setFilters({ ...filters, location: [`${selection}`] });\n\n setIsFilterDropdownOpen(false);\n onFilterSelect();\n };\n\n const buildCategoryDropdown = () => {\n const categoryMenuItems = [\n \n Location\n ,\n \n Name\n ,\n \n Status\n \n ];\n\n return (\n \n ) => (\n }\n style={\n {\n width: '100%',\n verticalAlign: 'text-bottom'\n } as React.CSSProperties\n }\n >\n {currentCategory}\n \n )}\n isOpen={isCategoryDropdownOpen}\n >\n {categoryMenuItems}\n \n \n );\n };\n\n const buildFilterDropdown = () => {\n const locationMenuItems = [\n \n Raleigh\n ,\n \n Westford\n ,\n \n Boston\n ,\n \n Brno\n ,\n \n Bangalore\n \n ];\n\n const statusMenuItems = [\n \n Running\n ,\n \n Stopped\n ,\n \n Down\n ,\n \n Degraded\n ,\n \n Needs maintenance\n \n ];\n\n return (\n \n onDelete(category, chip as string)}\n categoryName=\"Location\"\n showToolbarItem={currentCategory === 'Location'}\n >\n ) => (\n \n {filters.location[0] || `Any`}\n \n )}\n >\n {locationMenuItems}\n \n \n onDelete(category, chip as string)}\n categoryName=\"Name\"\n showToolbarItem={currentCategory === 'Name'}\n >\n onInputChange(value)}\n value={inputValue}\n onClear={() => {\n onInputChange('');\n }}\n onSearch={onNameInput} // any typing is needed because of what I think is a bug in the SearchInput typing\n />\n \n onDelete(category, chip as string)}\n categoryName=\"Status\"\n showToolbarItem={currentCategory === 'Status'}\n >\n ) => (\n \n Filter by status\n {filters.status.length > 0 && {filters.status.length}}\n \n )}\n >\n {statusMenuItems}\n \n \n \n );\n };\n\n const renderToolbar = () => (\n setFilters({ location: [], name: [], status: [] })}\n collapseListedFiltersBreakpoint=\"xl\"\n >\n \n } breakpoint=\"xl\">\n \n {buildCategoryDropdown()}\n {buildFilterDropdown()}\n \n \n \n \n );\n\n const measurementCache = new CellMeasurerCache({\n fixedWidth: true,\n minHeight: 44,\n keyMapper: (rowIndex) => rowIndex\n });\n\n const filteredRows =\n filters.name.length > 0 || filters.location.length > 0 || filters.status.length > 0\n ? rows.filter(\n (row) =>\n (filters.name.length === 0 ||\n filters.name.some((name) => (row.cells[0] as string).toLowerCase().includes(name.toLowerCase()))) &&\n (filters.location.length === 0 || filters.location.includes(row.cells[4] as string)) &&\n (filters.status.length === 0 || filters.status.includes(row.cells[3] as string))\n )\n : rows;\n\n const emptyState = (\n \n }\n />\n No results match the filter criteria. Clear all filters and try again.\n \n \n {\n setFilters({ location: [], name: [], status: [] });\n }}\n >\n Clear all filters\n \n \n \n \n );\n\n const rowRenderer = ({ index: rowIndex, _isScrolling, key, style, parent }) => (\n \n \n {columns.map((col, index) => (\n {filteredRows[rowIndex].cells[index]}\n ))}\n \n \n \n \n \n );\n\n interface ScrollableContainerStyle {\n height: number;\n overflowX: 'auto';\n overflowY: 'scroll';\n scrollBehavior: 'smooth';\n WebkitOverflowScrolling: 'touch';\n position: 'relative';\n }\n\n const scrollableContainerStyle: ScrollableContainerStyle = {\n height: 500 /* important note: the scrollable container should have some sort of fixed height, or it should be wrapped in container that is smaller than ReactVirtualized__VirtualGrid container and has overflow visible if using the Window Scroller. See WindowScroller.example.css */,\n overflowX: 'auto',\n overflowY: 'scroll',\n scrollBehavior: 'smooth',\n WebkitOverflowScrolling: 'touch',\n position: 'relative'\n };\n\n return (\n \n {renderToolbar()}\n \n \n \n {columns.map((col, index) => (\n \n ))}\n \n \n \n {filteredRows.length === 0 && (\n \n \n \n \n \n )}\n
{col}
\n {emptyState}\n
\n \n {({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => (\n \n {({ width }) => (\n
void}>\n \n
\n )}\n
\n )}\n
\n
\n );\n};\n","title":"Filterable with WindowScroller","lang":"js"}}> }; diff --git a/packages/module/patternfly-docs/generated/extensions/virtual-scroll-window-scroller/react.js b/packages/module/patternfly-docs/generated/extensions/virtual-scroll-window-scroller/react.js index 6a88167..24607e3 100644 --- a/packages/module/patternfly-docs/generated/extensions/virtual-scroll-window-scroller/react.js +++ b/packages/module/patternfly-docs/generated/extensions/virtual-scroll-window-scroller/react.js @@ -176,7 +176,9 @@ pageData.liveContext = { TableDeprecated, TableHeaderDeprecated }; -pageData.relativeImports = "import 'content/examples/./VirtualGrid.example.css';,import 'content/examples/./WindowScroller.example.css';" +pageData.relativeImports = { + +}; pageData.examples = { 'Window scroller': props => rowIndex\n });\n }\n\n this.state = {\n scrollToIndex: -1, // can be used to programmatically set current index\n scrollableElement: null,\n columns: [\n {\n title: 'Repositories',\n props: { className: 'pf-m-6-col-on-sm pf-m-4-col-on-md pf-m-3-col-on-lg pf-m-2-col-on-xl' }\n },\n {\n title: 'Branches',\n props: { className: 'pf-m-6-col-on-sm pf-m-4-col-on-md pf-m-3-col-on-lg pf-m-2-col-on-xl' }\n },\n {\n title: 'Pull requests',\n props: { className: 'pf-m-4-col-on-md pf-m-4-col-on-lg pf-m-3-col-on-xl pf-m-hidden pf-m-visible-on-md' }\n },\n {\n title: 'Workspaces',\n props: { className: 'pf-m-2-col-on-lg pf-m-2-col-on-xl pf-m-hidden pf-m-visible-on-lg' }\n },\n { title: 'Last Commit', props: { className: 'pf-m-3-col-on-xl pf-m-hidden pf-m-visible-on-xl' } }\n ],\n rows\n };\n\n this._handleResize = debounce(this._handleResize.bind(this), 100);\n this._bindBodyRef = this._bindBodyRef.bind(this);\n }\n\n componentDidMount() {\n // re-render after resize\n window.addEventListener('resize', this._handleResize);\n\n this.setState({ scrollableElement: document.getElementById('content-scrollable-1') });\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this._handleResize);\n }\n\n _handleResize() {\n this._cellMeasurementCache.clearAll();\n this._bodyRef.recomputeVirtualGridSize();\n }\n\n _bindBodyRef(ref) {\n this._bodyRef = ref;\n }\n\n render() {\n const { scrollToIndex, columns, rows, scrollableElement } = this.state;\n\n const rowRenderer = ({ index, _isScrolling, key, style, parent }) => {\n const { rows, columns } = this.state;\n const text = rows[index].cells[0];\n\n return (\n \n \n \n {text}\n \n \n {text}\n \n \n {text}\n \n \n {text}\n \n \n {text}\n \n \n \n );\n };\n\n return (\n \n
\n \n \n \n {scrollableElement && (\n \n {({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => (\n \n {({ width }) => (\n
\n \n
\n )}\n
\n )}\n
\n )}\n
\n \n );\n }\n}\n","title":"Window scroller","lang":"js"}}> diff --git a/packages/module/patternfly-docs/generated/index.js b/packages/module/patternfly-docs/generated/index.js index 7a3054e..c96b05f 100644 --- a/packages/module/patternfly-docs/generated/index.js +++ b/packages/module/patternfly-docs/generated/index.js @@ -13,8 +13,8 @@ module.exports = { '/extensions/virtual-scroll-window-scroller/react': { id: "Virtual scroll window scroller", title: "Virtual scroll window scroller", - toc: [{"text":"Examples"},[{"text":"Window scroller"}]], - examples: ["Window scroller"], + toc: [{"text":"Examples"},[{"text":"Window scroller"},{"text":"Using composable table components"}]], + examples: ["Window scroller","Using composable table components"], section: "extensions", subsection: "", source: "react",