diff --git a/README.md b/README.md index 013f955..da2fdd3 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ export interface MyComponentProps { const useStyles = createUseStyles({ myText: { fontFamily: 'monospace', - fontSize: 'var(--pf-v5-global--icon--FontSize--md)', + fontSize: 'var(--pf-v6-global--icon--FontSize--md)', }, }) @@ -126,7 +126,7 @@ When adding/making changes to a component, always make sure your code is tested: ### Styling: - for styling always use JSS - new classNames should be named in camelCase starting with the name of a given component and following with more details clarifying its purpose/component's subsection to which the class is applied (`actionMenu`, `actionMenuDropdown`, `actionMenuDropdownToggle`, etc.) -- do not use `pf-v5-u-XXX` classes, use CSS variables in a custom class instead (styles for the utility classes are not bundled with the standard patternfly.css - it would require the consumer to import also addons.css) +- do not use `pf-v6-u-XXX` classes, use CSS variables in a custom class instead (styles for the utility classes are not bundled with the standard patternfly.css - it would require the consumer to import also addons.css) --- diff --git a/cypress/component/DataViewCheckboxFilter.cy.tsx b/cypress/component/DataViewCheckboxFilter.cy.tsx new file mode 100644 index 0000000..eab26fb --- /dev/null +++ b/cypress/component/DataViewCheckboxFilter.cy.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { DataViewCheckboxFilter, DataViewCheckboxFilterProps } from '@patternfly/react-data-view/dist/dynamic/DataViewCheckboxFilter'; +import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; + +describe('DataViewCheckboxFilter component', () => { + const defaultProps: DataViewCheckboxFilterProps = { + filterId: 'test-checkbox-filter', + title: 'Test checkbox filter', + value: [ 'workspace-one' ], + options: [ + { label: 'Workspace one', value: 'workspace-one' }, + { label: 'Workspace two', value: 'workspace-two' }, + { label: 'Workspace three', value: 'workspace-three' }, + ], + }; + + it('renders a checkbox filter with options', () => { + const onChange = cy.stub().as('onChange'); + + cy.mount( + } /> + ); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]') + .contains('Test checkbox filter') + .should('be.visible'); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-badge"]') + .should('exist') + .contains('1'); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]').click(); + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]').should('be.visible'); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]') + .find('li') + .should('have.length', 3) + .first() + .contains('Workspace one'); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]') + .find('li') + .first() + .find('input[type="checkbox"]') + .should('be.checked'); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-menu"]') + .find('li') + .eq(1) + .find('input[type="checkbox"]') + .click(); + + cy.get('@onChange').should('have.been.calledWith', Cypress.sinon.match.object, [ 'workspace-two', 'workspace-one' ]); + }); + + it('renders a checkbox filter with no options selected', () => { + const emptyProps = { ...defaultProps, value: [] }; + + cy.mount( + } /> + ); + + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-toggle"]').contains('Test checkbox filter'); + cy.get('[data-ouia-component-id="DataViewCheckboxFilter-badge"]').should('not.exist'); + }); +}); diff --git a/cypress/component/DataViewFilters.cy.tsx b/cypress/component/DataViewFilters.cy.tsx new file mode 100644 index 0000000..b0cdea1 --- /dev/null +++ b/cypress/component/DataViewFilters.cy.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { useDataViewFilters } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { DataViewFilters } from '@patternfly/react-data-view/dist/dynamic/DataViewFilters'; +import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter'; +import { DataViewToolbar } from '@patternfly/react-data-view/dist/esm/DataViewToolbar'; +import { FilterIcon } from '@patternfly/react-icons'; + +const filtersProps = { + ouiaId: 'DataViewFilters', + toggleIcon: , + values: { name: '', branch: '' } +}; + +interface RepositoryFilters { + name: string, + branch: string +}; + +const DataViewToolbarWithState = (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any + const { filters, onSetFilters, clearAllFilters } = useDataViewFilters({ initialFilters: { name: '', branch: '' } }); + + return ( + onSetFilters(values)} values={filters} {...props}> + + + + } + /> + ); +}; + +describe('DataViewFilters', () => { + it('renders DataViewFilters with menu and filter items', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"]').should('exist'); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + + cy.contains('Name').should('exist'); + cy.contains('Branch').should('exist'); + }); + + it('can select a filter option', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Name'); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Branch').click(); + + cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Branch'); + }); + + it('responds to input and clears the filters', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Name').click(); + + cy.get('input[placeholder="Filter by name"]').type('Repository one'); + cy.get('.pf-v6-c-label__text').should('have.length', 1); + cy.get('input[placeholder="Filter by name"]').clear(); + cy.get('.pf-v6-c-label__text').should('have.length', 0); + }); + + it('displays labels for selected filters', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Name').click(); + cy.get('input[placeholder="Filter by name"]').type('Repository one'); + + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Branch').click(); + cy.get('input[placeholder="Filter by branch"]').type('Main branch'); + + cy.get('.pf-v6-c-label__text').should('have.length', 2); + cy.get('.pf-v6-c-label__text').eq(0).should('contain.text', 'Repository one'); + cy.get('.pf-v6-c-label__text').eq(1).should('contain.text', 'Main branch'); + }); + + it('removes filters by clicking individual labels', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Name').click(); + cy.get('input[placeholder="Filter by name"]').type('Repository one'); + + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Branch').click(); + cy.get('input[placeholder="Filter by branch"]').type('Main branch'); + + cy.get('[aria-label="Close Repository one"]').should('have.length', 1); + cy.get('[aria-label="Close Main branch"]').should('have.length', 1); + + cy.get('[aria-label="Close Repository one"]').click(); + cy.get('[aria-label="Close Repository one"]').should('have.length', 0); + + cy.get('[aria-label="Close Main branch"]').click(); + cy.get('[aria-label="Close Main branch"]').should('have.length', 0); + }); + + it('clears all filters using the clear-all button', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Name').click(); + cy.get('input[placeholder="Filter by name"]').type('Repository one'); + + cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click(); + cy.contains('Branch').click(); + cy.get('input[placeholder="Filter by branch"]').type('Main branch'); + + cy.get('[data-ouia-component-id="FiltersExampleHeader-clear-all-filters"]').should('exist').click(); + }); +}); diff --git a/cypress/component/DataViewTableSorting.cy.tsx b/cypress/component/DataViewTableSorting.cy.tsx new file mode 100644 index 0000000..6800055 --- /dev/null +++ b/cypress/component/DataViewTableSorting.cy.tsx @@ -0,0 +1,117 @@ +/* eslint-disable no-nested-ternary */ +import React from 'react'; +import { useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; +import { BrowserRouter, useSearchParams } from 'react-router-dom'; +import { ThProps } from '@patternfly/react-table'; + +interface Repository { + name: string; + branches: string; + prs: string; + workspaces: string; + lastCommit: string; +} + +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 repositories: Repository[] = [ + { name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: '2023-11-01' }, + { name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: '2023-11-06' }, + { name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: '2023-11-02' }, + { name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: '2023-11-05' }, + { name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: '2023-11-03' }, + { name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: '2023-11-04' }, +]; + +const sortData = (data: Repository[], sortBy: keyof Repository | 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 TestTable: React.FunctionComponent = () => { + const [ searchParams, setSearchParams ] = useSearchParams(); + const { sortBy, direction, onSort } = useDataViewSort({ searchParams, setSearchParams }); + const sortByIndex = React.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 rows: DataViewTr[] = React.useMemo( + () => + sortData(repositories, sortBy ? sortBy as keyof Repository : undefined, direction).map(({ name, branches, prs, workspaces, lastCommit }) => [ + name, + branches, + prs, + workspaces, + lastCommit, + ]), + [ sortBy, direction ] + ); + + return ; +}; + +describe('DataViewTable Sorting with Hook', () => { + it('sorts by repository name in ascending and descending order', () => { + cy.mount( + + + + ); + + cy.get('[data-ouia-component-id="test-table-th-0"]') + .find('button') + .click(); + cy.get('[data-ouia-component-id="test-table-td-0-0"]').should('contain', 'Repository five'); + cy.get('[data-ouia-component-id="test-table-td-5-0"]').should('contain', 'Repository two'); + + cy.get('[data-ouia-component-id="test-table-th-0"]') + .find('button') + .click(); + cy.get('[data-ouia-component-id="test-table-td-0-0"]').should('contain', 'Repository two'); + cy.get('[data-ouia-component-id="test-table-td-5-0"]').should('contain', 'Repository five'); + }); + + it('sorts by last commit date in ascending and descending order', () => { + cy.mount( + + + + ); + + cy.get('[data-ouia-component-id="test-table-th-4"]') + .find('button') + .click(); + cy.get('[data-ouia-component-id="test-table-td-0-4"]').should('contain', '2023-11-01'); + cy.get('[data-ouia-component-id="test-table-td-5-4"]').should('contain', '2023-11-06'); + + cy.get('[data-ouia-component-id="test-table-th-4"]') + .find('button') + .click(); + cy.get('[data-ouia-component-id="test-table-td-0-4"]').should('contain', '2023-11-06'); + cy.get('[data-ouia-component-id="test-table-td-5-4"]').should('contain', '2023-11-01'); + }); +}); diff --git a/cypress/component/DataViewTextFilter.cy.tsx b/cypress/component/DataViewTextFilter.cy.tsx new file mode 100644 index 0000000..c8c7215 --- /dev/null +++ b/cypress/component/DataViewTextFilter.cy.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import { DataViewTextFilter } from '@patternfly/react-data-view/dist/dynamic/DataViewTextFilter'; +import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; + +const defaultProps = { + filterId: 'name', + title: 'Name', + value: '', + ouiaId: 'DataViewTextFilter', + placeholder: 'Filter by name' +}; + +const DataViewToolbarWithState = (props: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any + const [ value, setValue ] = useState('Repository one'); + + return ( + setValue('')}> + setValue('')} {...props} /> + + ); +}; + +describe('DataViewTextFilter', () => { + + it('renders DataViewTextFilter with correct initial values', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('exist'); + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input') + .should('have.attr', 'placeholder', 'Filter by name') + .and('have.value', ''); + }); + + it('accepts input when passed', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input') + .type('Repository one') + .should('have.value', 'Repository one'); + }); + + it('displays a label when value is present and removes it on delete', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', 'Repository one'); + + cy.get('.pf-v6-c-label__text').contains('Repository one'); + cy.get('.pf-m-label-group button.pf-v6-c-button.pf-m-plain').click(); + + cy.get('.pf-v6-c-label__text').should('not.exist'); + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', ''); + }); + + it('clears input when the clear button is clicked', () => { + cy.mount(); + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', 'Repository one'); + + cy.get('[data-ouia-component-id="DataViewToolbar-clear-all-filters"]').click(); + + cy.get('[data-ouia-component-id="DataViewTextFilter-input"] input').should('have.value', ''); + }); + + it('hides or shows the toolbar item based on showToolbarItem prop', () => { + cy.mount( + + + + ); + cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('not.exist'); + + cy.mount( + + + + ); + cy.get('[data-ouia-component-id="DataViewTextFilter"]').should('exist'); + }); +}); diff --git a/package-lock.json b/package-lock.json index f0480dd..0cb7794 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,10 @@ "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@octokit/rest": "^20.1.1", - "@patternfly/documentation-framework": "6.0.0-alpha.117", - "@swc/core": "1.6.1", + "@patternfly/documentation-framework": "6.0.6", + "@swc/core": "1.9.1", "@testing-library/dom": "^10.1.0", - "@testing-library/jest-dom": "^6.4.6", + "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "14.5.2", "@types/jest": "^29.5.13", @@ -31,18 +31,18 @@ "babel-jest": "^29.7.0", "babel-polyfill": "6.26.0", "chokidar": "^3.6.0", - "concurrently": "^8.2.2", - "cypress": "^13.11.0", + "concurrently": "^9.1.0", + "cypress": "^13.15.2", "eslint": "^8.57.0", "eslint-config-prettier": "9.1.0", "eslint-config-standard-with-typescript": "^23.0.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-markdown": "^1.0.2", "eslint-plugin-n": "^15.7.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-promise": "^6.2.0", "eslint-plugin-react": "^7.34.3", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-hooks": "^5.0.0", "fs-extra": "^11.2.0", "glob": "^10.4.1", "identity-obj-proxy": "^3.0.0", @@ -53,7 +53,7 @@ "react": "^18", "react-dom": "^18", "rimraf": "^5.0.7", - "sass": "^1.77.6", + "sass": "^1.80.7", "sass-loader": "^14.2.1", "serve": "^14.2.3", "start-server-and-test": "^2.0.4", @@ -2068,9 +2068,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", - "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.6.tgz", + "integrity": "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2089,7 +2089,7 @@ "performance-now": "^2.1.0", "qs": "6.13.0", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -2097,6 +2097,19 @@ "node": ">= 6" } }, + "node_modules/@cypress/request/node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -2144,17 +2157,20 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -3517,6 +3533,7 @@ "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -3798,9 +3815,9 @@ } }, "node_modules/@patternfly/ast-helpers": { - "version": "1.4.0-alpha.106", - "resolved": "https://registry.npmjs.org/@patternfly/ast-helpers/-/ast-helpers-1.4.0-alpha.106.tgz", - "integrity": "sha512-5Xfi2r2oudTvPYjoMGlJKSDlAf95GCTr8ely/F/pNiyV28EdNPJ9cKIVWstZex1wxvyxtvp5wOfpRh2fWE1Khg==", + "version": "1.4.0-alpha.125", + "resolved": "https://registry.npmjs.org/@patternfly/ast-helpers/-/ast-helpers-1.4.0-alpha.125.tgz", + "integrity": "sha512-WvN5CYFmTYOztt3wMidJq8xLdu6jhpHzjPvYxMN2wlh7v0QJEHaplvJGZH/E8ZUcri/ni2OoLVIe2KcuRwrxng==", "dev": true, "license": "MIT", "dependencies": { @@ -3812,9 +3829,9 @@ } }, "node_modules/@patternfly/documentation-framework": { - "version": "6.0.0-alpha.117", - "resolved": "https://registry.npmjs.org/@patternfly/documentation-framework/-/documentation-framework-6.0.0-alpha.117.tgz", - "integrity": "sha512-npVzrFElVEuDBSvu4yiF5BK3c1x9RwjD1G+bbtLbbb8uffPul88zeThuA7vpi02sQc1z1cV6XGJo6ZZ8OQ1OjQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@patternfly/documentation-framework/-/documentation-framework-6.0.6.tgz", + "integrity": "sha512-YXXzj7K2lmAZbvgTDSTd4AJTW6xpJ6G/C3jCMxGGMXevYJ1wGHQQnBVbGQvQcZPgIwU5nv3+FmnyfldJhRwQtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3822,7 +3839,7 @@ "@babel/preset-env": "^7.24.3", "@babel/preset-react": "^7.24.1", "@mdx-js/util": "1.6.16", - "@patternfly/ast-helpers": "^1.4.0-alpha.106", + "@patternfly/ast-helpers": "^1.4.0-alpha.122", "@reach/router": "npm:@gatsbyjs/reach-router@1.3.9", "autoprefixer": "9.8.6", "babel-loader": "^9.1.3", @@ -3885,10 +3902,10 @@ "pf-docs-framework": "scripts/cli/cli.js" }, "peerDependencies": { - "@patternfly/patternfly": "6.0.0-prerelease.15", - "@patternfly/react-code-editor": "6.0.0-prerelease.21", - "@patternfly/react-core": "6.0.0-prerelease.21", - "@patternfly/react-table": "6.0.0-prerelease.22", + "@patternfly/patternfly": "^6.0.0", + "@patternfly/react-code-editor": "^6.0.0", + "@patternfly/react-core": "^6.0.0", + "@patternfly/react-table": "^6.0.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" } @@ -4296,265 +4313,260 @@ } }, "node_modules/@patternfly/patternfly": { - "version": "6.0.0-prerelease.15", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.0.0-prerelease.15.tgz", - "integrity": "sha512-7/tRjaWBMX9bvMhIexdAONgxKiVbOCuFffLEKq+p+Q4t8Qq8fqGM7eAznzOx3YbLKIzqVtmbRrgTAIkpsSe1aw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.0.0.tgz", + "integrity": "sha512-Mn92Tt/4okSj1COGCJrgUgh390OOaFCWf0tL0WmigDNUecSHNn1D6Vhpd1hxHQBXvre9eWorzxV2b9yhSEl79Q==", "dev": true, "license": "MIT" }, "node_modules/@patternfly/patternfly-a11y": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly-a11y/-/patternfly-a11y-4.3.1.tgz", - "integrity": "sha512-WBdiCJsfEo+cLgtLPDyvOtvRewzWqUvymLOx+Hj/jFoMI7hbotoZSBl4GUZDtFDOHXN4/vfu16eZy9DQHTY/Ew==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly-a11y/-/patternfly-a11y-5.0.0.tgz", + "integrity": "sha512-2GSzO6rSo/8Qg5IBvZeRtJp9tLj6XZGMD0HkENMBkHKDXBdUvCLftsu91E9408qAqgxDx1DrO3nhB3spo5LfiA==", "dev": true, "license": "MIT", "dependencies": { - "axe-core": "^4.4.1", - "chromedriver": "^101.0.0", - "commander": "^5.1.0", - "fs-extra": "^10.0.0", + "axe-core": "^4.10.2", + "chromedriver": "^130.0.1", + "commander": "^12.1.0", + "fs-extra": "^11.2.0", "junit-xml": "^1.2.0", - "puppeteer": "^14.2.0", - "puppeteer-cluster": "^0.23.0", - "xmldoc": "^1.1.2" + "puppeteer": "^23.6.1", + "puppeteer-cluster": "^0.24.0", + "xmldoc": "^1.3.0" }, "bin": { "patternfly-a11y": "cli.js" + }, + "peerDependencies": { + "victory-bar": "^37.1.1", + "victory-core": "^37.1.1", + "victory-create-container": "^37.1.1", + "victory-cursor-container": "^37.1.1", + "victory-group": "^37.1.1", + "victory-legend": "^37.1.1", + "victory-line": "^37.1.1", + "victory-pie": "^37.1.1", + "victory-scatter": "^37.1.1", + "victory-stack": "^37.1.1", + "victory-tooltip": "^37.1.1", + "victory-voronoi-container": "^37.1.1" + } + }, + "node_modules/@patternfly/patternfly-a11y/node_modules/@puppeteer/browsers": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.1.tgz", + "integrity": "sha512-0kdAbmic3J09I6dT8e9vE2JOCSt13wHCW5x/ly8TSt2bDtuIWe2TgLZZDHdcziw9AVCzflMAXCrVyRIhIs44Ng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.3.7", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/@patternfly/patternfly-a11y/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", + "license": "Python-2.0" + }, + "node_modules/@patternfly/patternfly-a11y/node_modules/chromium-bidi": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz", + "integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" + }, + "peerDependencies": { + "devtools-protocol": "*" } }, "node_modules/@patternfly/patternfly-a11y/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=18" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@patternfly/patternfly-a11y/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": ">=6.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" }, "peerDependenciesMeta": { - "supports-color": { + "typescript": { "optional": true } } }, "node_modules/@patternfly/patternfly-a11y/node_modules/devtools-protocol": { - "version": "0.0.1001819", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1001819.tgz", - "integrity": "sha512-G6OsIFnv/rDyxSqBa2lDLR6thp9oJioLsb2Gl+LbQlyoA9/OBAkrTU9jiCcQ8Pnh7z4d6slDiLaogR5hzgJLmQ==", + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@patternfly/patternfly-a11y/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/@patternfly/patternfly-a11y/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@patternfly/patternfly-a11y/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/@patternfly/patternfly-a11y/node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true, "license": "MIT" }, - "node_modules/@patternfly/patternfly-a11y/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/@patternfly/patternfly-a11y/node_modules/puppeteer": { + "version": "23.9.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.9.0.tgz", + "integrity": "sha512-WfB8jGwFV+qrD9dcJJVvWPFJBU6kxeu2wxJz9WooDGfM3vIiKLgzImEDBxUQnCBK/2cXB3d4dV6gs/LLpgfLDg==", "dev": true, - "license": "MIT", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "find-up": "^4.0.0" + "@puppeteer/browsers": "2.4.1", + "chromium-bidi": "0.8.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1367902", + "puppeteer-core": "23.9.0", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@patternfly/patternfly-a11y/node_modules/puppeteer": { - "version": "14.4.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-14.4.1.tgz", - "integrity": "sha512-+H0Gm84aXUvSLdSiDROtLlOofftClgw2TdceMvvCU9UvMryappoeS3+eOLfKvoy4sm8B8MWnYmPhWxVFudAOFQ==", - "deprecated": "< 22.8.2 is no longer supported", + "node_modules/@patternfly/patternfly-a11y/node_modules/puppeteer-cluster": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/puppeteer-cluster/-/puppeteer-cluster-0.24.0.tgz", + "integrity": "sha512-zHPoNsrwkFLKFtgJJv2aC13EbMASQsE048uZd7CyikEXcl+sc1Nf6PMFb9kMoZI7/zMYxvuP658o2mw079ZZyQ==", "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "cross-fetch": "3.1.5", - "debug": "4.3.4", - "devtools-protocol": "0.0.1001819", - "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.1", - "pkg-dir": "4.2.0", - "progress": "2.0.3", - "proxy-from-env": "1.1.0", - "rimraf": "3.0.2", - "tar-fs": "2.1.1", - "unbzip2-stream": "1.4.3", - "ws": "8.7.0" + "debug": "^4.3.4" }, - "engines": { - "node": ">=14.1.0" + "peerDependencies": { + "puppeteer": ">=22.0.0" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/@patternfly/patternfly-a11y/node_modules/puppeteer-core": { + "version": "23.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.9.0.tgz", + "integrity": "sha512-hLVrav2HYMVdK0YILtfJwtnkBAwNOztUdR4aJ5YKDvgsbtagNr6urUJk9HyjRA9e+PaLI3jzJ0wM7A4jSZ7Qxw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@puppeteer/browsers": "2.4.1", + "chromium-bidi": "0.8.0", + "debug": "^4.3.7", + "devtools-protocol": "0.0.1367902", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" }, "engines": { - "node": ">= 6" + "node": ">=18" } }, - "node_modules/@patternfly/patternfly-a11y/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/@patternfly/patternfly-a11y/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, "bin": { - "rimraf": "bin.js" + "semver": "bin/semver.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=10" } }, "node_modules/@patternfly/patternfly-a11y/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, "license": "MIT", "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/@patternfly/patternfly-a11y/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@patternfly/patternfly-a11y/node_modules/ws": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz", - "integrity": "sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, "node_modules/@patternfly/react-code-editor": { - "version": "6.0.0-prerelease.21", - "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.0.0-prerelease.21.tgz", - "integrity": "sha512-t9/8Uk3sbPaXasZXHaIxvcAGRWAlep9L0Gsy1vA+vzmpU8Igk1GO2JNMVr9ux4ScLEuMnzp0Rbq++VbxtDNdwA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.0.0.tgz", + "integrity": "sha512-TnI/NNkizzWTzdVZWmpyEPKXgsOoUeklk8Xlgtl7II/+5juLjlt0wXTMhL35F59Rzd0YohGs251zXAwJbn6vIQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@monaco-editor/react": "^4.6.0", - "@patternfly/react-core": "^6.0.0-prerelease.21", - "@patternfly/react-icons": "^6.0.0-prerelease.7", - "@patternfly/react-styles": "^6.0.0-prerelease.6", + "@patternfly/react-core": "^6.0.0", + "@patternfly/react-icons": "^6.0.0", + "@patternfly/react-styles": "^6.0.0", "react-dropzone": "14.2.3", "tslib": "^2.7.0" }, @@ -4564,14 +4576,14 @@ } }, "node_modules/@patternfly/react-component-groups": { - "version": "6.0.0-prerelease.7", - "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.0.0-prerelease.7.tgz", - "integrity": "sha512-+JYabo0L8ASWSiYHA6mzDrAdPABdcmaEk2Ts06PuGb/nrZdOLvdAHRFpzcnjFva9+tkeItHvAXAT7bK5WVAEEg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.0.0.tgz", + "integrity": "sha512-q4dQj4b/Yf8fW3ZXWZhXrX8xE73JpRAFF5wydDx6OZe3oZXucUgldghupavbyhY/oYM2V89/P67w24poH6iOqA==", "license": "MIT", "dependencies": { - "@patternfly/react-core": "^6.0.0-prerelease.21", - "@patternfly/react-icons": "^6.0.0-prerelease.7", - "@patternfly/react-table": "^6.0.0-prerelease.22", + "@patternfly/react-core": "^6.0.0", + "@patternfly/react-icons": "^6.0.0", + "@patternfly/react-table": "^6.0.0", "clsx": "^2.1.1", "react-jss": "^10.10.0" }, @@ -4581,14 +4593,14 @@ } }, "node_modules/@patternfly/react-core": { - "version": "6.0.0-prerelease.21", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.0.0-prerelease.21.tgz", - "integrity": "sha512-EaGcKUPeeR253vY4N0Ahm9oOVtltoI6JycfclwmzjevOzpYvuLj1jcsVwL8wqgWYQVpURoBm1yxIdx34fo5UHA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.0.0.tgz", + "integrity": "sha512-UKFj9+YzBY+FfEDsLONgOM4N0e8SPV/27/UzNRiJ0gpgqbw2POuXwLpjGSRTTIUuCaLaGGM5PeTSj7mMB73ykw==", "license": "MIT", "dependencies": { - "@patternfly/react-icons": "^6.0.0-prerelease.7", - "@patternfly/react-styles": "^6.0.0-prerelease.6", - "@patternfly/react-tokens": "^6.0.0-prerelease.7", + "@patternfly/react-icons": "^6.0.0", + "@patternfly/react-styles": "^6.0.0", + "@patternfly/react-tokens": "^6.0.0", "focus-trap": "7.6.0", "react-dropzone": "^14.2.3", "tslib": "^2.7.0" @@ -4603,9 +4615,9 @@ "link": true }, "node_modules/@patternfly/react-icons": { - "version": "6.0.0-prerelease.7", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.0.0-prerelease.7.tgz", - "integrity": "sha512-DQmecVgXRIiD3ww4KUuJ0qO76TmYMDEJ1ao1+DYuTSP+FzeJLJKuE9QxvL8qn3anyKtuORBuHdTIjM52mVq5Vg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.0.0.tgz", + "integrity": "sha512-ZFrsBVKrAp0DZrPOss98OA/EVUL4F0frXhR1uBId9+3ZrRArdKTgYgmQUCeSzMbxnSlxpmm3a2L05XQ36VUVbw==", "license": "MIT", "peerDependencies": { "react": "^17 || ^18", @@ -4613,21 +4625,21 @@ } }, "node_modules/@patternfly/react-styles": { - "version": "6.0.0-prerelease.6", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.0.0-prerelease.6.tgz", - "integrity": "sha512-tI28gIJFgbgVQs7Xj705csfl6T92dr5Bh7ynR5gN4+QdTWCUWmSctp46G2ZewXdrIN+C+2zUPE86o77aFp4CWw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.0.0.tgz", + "integrity": "sha512-fJFMB89sTRGlZTzTLmpRmthgOXqcN078scHMFJ3ttfi2D2btnem5oZrxmQ/gPZkZOxR+9MqwKDB6l3F5x1SqLQ==", "license": "MIT" }, "node_modules/@patternfly/react-table": { - "version": "6.0.0-prerelease.22", - "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.0.0-prerelease.22.tgz", - "integrity": "sha512-vGDWr14YATIY9RnxaOpyjkPlw4aKBM7dhHJRPXY0cwCFehSGwwzQpialZyi+92I2WfBF1Cb75doDAtxTSi8UZQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.0.0.tgz", + "integrity": "sha512-LvWMzjcQZHdFUpK8fjj5EAFrNxqB8/MFd7gUUZu7AgYt6rmS2im4xk6yb7h0K7cAhY085oPeRF9lkYSCgzlRDg==", "license": "MIT", "dependencies": { - "@patternfly/react-core": "^6.0.0-prerelease.21", - "@patternfly/react-icons": "^6.0.0-prerelease.7", - "@patternfly/react-styles": "^6.0.0-prerelease.6", - "@patternfly/react-tokens": "^6.0.0-prerelease.7", + "@patternfly/react-core": "^6.0.0", + "@patternfly/react-icons": "^6.0.0", + "@patternfly/react-styles": "^6.0.0", + "@patternfly/react-tokens": "^6.0.0", "lodash": "^4.17.21", "tslib": "^2.7.0" }, @@ -4637,9 +4649,9 @@ } }, "node_modules/@patternfly/react-tokens": { - "version": "6.0.0-prerelease.7", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.0.0-prerelease.7.tgz", - "integrity": "sha512-SLgVwbIgVx26LCjaXkpNlPIZYqWpHJkw3QX/n3URLmIcRlCw536/rKO1PzXaeuCCqhuSq66J6R125zM2eJjM6A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.0.0.tgz", + "integrity": "sha512-xd0ynDkiIW2rp8jz4TNvR4Dyaw9kSMkZdsuYcLlFXCVmvX//Mnl4rhBnid/2j2TaqK0NbkyTTPnPY/BU7SfLVQ==", "license": "MIT" }, "node_modules/@pkgjs/parseargs": { @@ -4840,9 +4852,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", - "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", "dev": true, "license": "MIT", "engines": { @@ -4908,15 +4920,15 @@ } }, "node_modules/@swc/core": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.6.1.tgz", - "integrity": "sha512-Yz5uj5hNZpS5brLtBvKY0L4s2tBAbQ4TjmW8xF1EC3YLFxQRrUjMP49Zm1kp/KYyYvTkSaG48Ffj2YWLu9nChw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.9.1.tgz", + "integrity": "sha512-OnPc+Kt5oy3xTvr/KCUOqE9ptJcWbyQgAUr1ydh9EmbBcmJTaO1kfQCxm/axzJi6sKeDTxL9rX5zvLOhoYIaQw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.8" + "@swc/types": "^0.1.14" }, "engines": { "node": ">=10" @@ -4926,16 +4938,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.6.1", - "@swc/core-darwin-x64": "1.6.1", - "@swc/core-linux-arm-gnueabihf": "1.6.1", - "@swc/core-linux-arm64-gnu": "1.6.1", - "@swc/core-linux-arm64-musl": "1.6.1", - "@swc/core-linux-x64-gnu": "1.6.1", - "@swc/core-linux-x64-musl": "1.6.1", - "@swc/core-win32-arm64-msvc": "1.6.1", - "@swc/core-win32-ia32-msvc": "1.6.1", - "@swc/core-win32-x64-msvc": "1.6.1" + "@swc/core-darwin-arm64": "1.9.1", + "@swc/core-darwin-x64": "1.9.1", + "@swc/core-linux-arm-gnueabihf": "1.9.1", + "@swc/core-linux-arm64-gnu": "1.9.1", + "@swc/core-linux-arm64-musl": "1.9.1", + "@swc/core-linux-x64-gnu": "1.9.1", + "@swc/core-linux-x64-musl": "1.9.1", + "@swc/core-win32-arm64-msvc": "1.9.1", + "@swc/core-win32-ia32-msvc": "1.9.1", + "@swc/core-win32-x64-msvc": "1.9.1" }, "peerDependencies": { "@swc/helpers": "*" @@ -4947,9 +4959,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.6.1.tgz", - "integrity": "sha512-u6GdwOXsOEdNAdSI6nWq6G2BQw5HiSNIZVcBaH1iSvBnxZvWbnIKyDiZKaYnDwTLHLzig2GuUjjE2NaCJPy4jg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.1.tgz", + "integrity": "sha512-2/ncHSCdAh5OHem1fMITrWEzzl97OdMK1PHc9CkxSJnphLjRubfxB5sbc5tDhcO68a5tVy+DxwaBgDec3PXnOg==", "cpu": [ "arm64" ], @@ -4964,9 +4976,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.6.1.tgz", - "integrity": "sha512-/tXwQibkDNLVbAtr7PUQI0iQjoB708fjhDDDfJ6WILSBVZ3+qs/LHjJ7jHwumEYxVq1XA7Fv2Q7SE/ZSQoWHcQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.1.tgz", + "integrity": "sha512-4MDOFC5zmNqRJ9RGFOH95oYf27J9HniLVpB1pYm2gGeNHdl2QvDMtx2QTuMHQ6+OTn/3y1BHYuhBGp7d405oLA==", "cpu": [ "x64" ], @@ -4981,9 +4993,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.6.1.tgz", - "integrity": "sha512-aDgipxhJTms8iH78emHVutFR2c16LNhO+NTRCdYi+X4PyIn58/DyYTH6VDZ0AeEcS5f132ZFldU5AEgExwihXA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.1.tgz", + "integrity": "sha512-eVW/BjRW8/HpLe3+1jRU7w7PdRLBgnEEYTkHJISU8805/EKT03xNZn6CfaBpKfeAloY4043hbGzE/NP9IahdpQ==", "cpu": [ "arm" ], @@ -4998,9 +5010,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.6.1.tgz", - "integrity": "sha512-XkJ+eO4zUKG5g458RyhmKPyBGxI0FwfWFgpfIj5eDybxYJ6s4HBT5MoxyBLorB5kMlZ0XoY/usUMobPVY3nL0g==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.1.tgz", + "integrity": "sha512-8m3u1v8R8NgI/9+cHMkzk14w87blSy3OsQPWPfhOL+XPwhyLPvat+ahQJb2nZmltjTgkB4IbzKFSfbuA34LmNA==", "cpu": [ "arm64" ], @@ -5015,9 +5027,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.6.1.tgz", - "integrity": "sha512-dr6YbLBg/SsNxs1hDqJhxdcrS8dGMlOXJwXIrUvACiA8jAd6S5BxYCaqsCefLYXtaOmu0bbx1FB/evfodqB70Q==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.1.tgz", + "integrity": "sha512-hpT0sQAZnW8l02I289yeyFfT9llGO9PzKDxUq8pocKtioEHiElRqR53juCWoSmzuWi+6KX7zUJ0NKCBrc8pmDg==", "cpu": [ "arm64" ], @@ -5032,9 +5044,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.6.1.tgz", - "integrity": "sha512-A0b/3V+yFy4LXh3O9umIE7LXPC7NBWdjl6AQYqymSMcMu0EOb1/iygA6s6uWhz9y3e172Hpb9b/CGsuD8Px/bg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.1.tgz", + "integrity": "sha512-sGFdpdAYusk/ropHiwtXom2JrdaKPxl8MqemRv6dvxZq1Gm/GdmOowxdXIPjCgBGMgoXVcgNviH6CgiO5q+UtA==", "cpu": [ "x64" ], @@ -5049,9 +5061,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.6.1.tgz", - "integrity": "sha512-5dJjlzZXhC87nZZZWbpiDP8kBIO0ibis893F/rtPIQBI5poH+iJuA32EU3wN4/WFHeK4et8z6SGSVghPtWyk4g==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.1.tgz", + "integrity": "sha512-YtNLNwIWs0Z2+XgBs6+LrCIGtfCDtNr4S4b6Q5HDOreEIGzSvhkef8eyBI5L+fJ2eGov4b7iEo61C4izDJS5RA==", "cpu": [ "x64" ], @@ -5066,9 +5078,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.6.1.tgz", - "integrity": "sha512-HBi1ZlwvfcUibLtT3g/lP57FaDPC799AD6InolB2KSgkqyBbZJ9wAXM8/CcH67GLIP0tZ7FqblrJTzGXxetTJQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.1.tgz", + "integrity": "sha512-qSxD3uZW2vSiHqUt30vUi0PB92zDh9bjqh5YKpfhhVa7h1vt/xXhlid8yMvSNToTfzhRrTEffOAPUr7WVoyQUA==", "cpu": [ "arm64" ], @@ -5083,9 +5095,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.6.1.tgz", - "integrity": "sha512-AKqHohlWERclexar5y6ux4sQ8yaMejEXNxeKXm7xPhXrp13/1p4/I3E5bPVX/jMnvpm4HpcKSP0ee2WsqmhhPw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.1.tgz", + "integrity": "sha512-C3fPEwyX/WRPlX6zIToNykJuz1JkZX0sk8H1QH2vpnKuySUkt/Ur5K2FzLgSWzJdbfxstpgS151/es0VGAD+ZA==", "cpu": [ "ia32" ], @@ -5100,9 +5112,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.6.1.tgz", - "integrity": "sha512-0dLdTLd+ONve8kgC5T6VQ2Y5G+OZ7y0ujjapnK66wpvCBM6BKYGdT/OKhZKZydrC5gUKaxFN6Y5oOt9JOFUrOQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.1.tgz", + "integrity": "sha512-2XZ+U1AyVsOAXeH6WK1syDm7+gwTjA8fShs93WcbxnK7HV+NigDlvr4124CeJLTHyh3fMh1o7+CnQnaBJhlysQ==", "cpu": [ "x64" ], @@ -5124,9 +5136,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", - "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5237,9 +5249,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", - "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", "dev": true, "license": "MIT", "dependencies": { @@ -5389,6 +5401,13 @@ "node": ">= 10" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -5483,6 +5502,87 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -7159,9 +7259,9 @@ "license": "MIT" }, "node_modules/axe-core": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", - "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true, "license": "MPL-2.0", "engines": { @@ -7550,6 +7650,16 @@ ], "license": "MIT" }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -8411,60 +8521,38 @@ } }, "node_modules/chromedriver": { - "version": "101.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-101.0.0.tgz", - "integrity": "sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w==", - "deprecated": "Chromedriver download url has changed. Use version 114.0.2 or newer.", + "version": "130.0.4", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-130.0.4.tgz", + "integrity": "sha512-lpR+PWXszij1k4Ig3t338Zvll9HtCTiwoLM7n4pCCswALHxzmgwaaIFBh3rt9+5wRk9D07oFblrazrBxwaYYAQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@testim/chrome-version": "^1.1.2", - "axios": "^0.24.0", - "del": "^6.0.0", + "@testim/chrome-version": "^1.1.4", + "axios": "^1.7.4", + "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", - "https-proxy-agent": "^5.0.0", + "proxy-agent": "^6.4.0", "proxy-from-env": "^1.1.0", - "tcp-port-used": "^1.0.1" + "tcp-port-used": "^1.0.2" }, "bin": { "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/chromedriver/node_modules/axios": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", - "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.14.4" - } - }, - "node_modules/chromedriver/node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "license": "MIT", "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/chromedriver/node_modules/follow-redirects": { @@ -8488,44 +8576,6 @@ } } }, - "node_modules/chromedriver/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/chromedriver/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/chromedriver/node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -8533,23 +8583,6 @@ "dev": true, "license": "MIT" }, - "node_modules/chromedriver/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/chromium-bidi": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.7.tgz", @@ -9127,6 +9160,13 @@ "dev": true, "license": "MIT" }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -9217,18 +9257,16 @@ } }, "node_modules/concurrently": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.0.tgz", + "integrity": "sha512-VxkzwMAn4LP7WyMnJNbHN5mKV9L2IbyDjpzemKr99sXNR3GqRNMMHdm7prV1ws9wg7ETj6WUkNOigZVsptwbgg==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", - "date-fns": "^2.30.0", "lodash": "^4.17.21", "rxjs": "^7.8.1", "shell-quote": "^1.8.1", - "spawn-command": "0.0.2", "supports-color": "^8.1.1", "tree-kill": "^1.2.2", "yargs": "^17.7.2" @@ -9238,7 +9276,7 @@ "concurrently": "dist/bin/concurrently.js" }, "engines": { - "node": "^14.13.0 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" @@ -9964,14 +10002,14 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", - "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.16.0.tgz", + "integrity": "sha512-g6XcwqnvzXrqiBQR/5gN+QsyRmKRhls1y5E42fyOvsmU7JuY+wM6uHJWj4ZPttjabzbnRvxcik2WemR8+xT6FA==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.4", + "@cypress/request": "^3.0.6", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -9982,6 +10020,7 @@ "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", + "ci-info": "^4.0.0", "cli-cursor": "^3.1.0", "cli-table3": "~0.6.1", "commander": "^6.2.1", @@ -9996,7 +10035,6 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -10011,6 +10049,7 @@ "semver": "^7.5.3", "supports-color": "^8.1.1", "tmp": "~0.2.3", + "tree-kill": "1.2.2", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -10067,6 +10106,22 @@ "node": ">=8" } }, + "node_modules/cypress/node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cypress/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10120,36 +10175,179 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/cypress/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cypress/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/cypress/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "peer": true, + "dependencies": { + "d3-array": "2 - 3" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "dev": true, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "d3-time": "1 - 3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" } }, "node_modules/dashdash": { @@ -10165,6 +10363,16 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -10260,23 +10468,6 @@ "semver": "bin/semver" } }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -10473,6 +10664,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/degenerator/node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -10568,6 +10787,25 @@ "rimraf": "bin.js" } }, + "node_modules/delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/delaunay-find": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/delaunay-find/-/delaunay-find-0.0.6.tgz", + "integrity": "sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "delaunator": "^4.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -10636,6 +10874,7 @@ "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, "license": "Apache-2.0", + "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -11030,6 +11269,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/envinfo": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", @@ -11383,7 +11632,27 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-standard": { + "node_modules/eslint-config-standard-with-typescript": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-23.0.0.tgz", + "integrity": "sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==", + "deprecated": "Please use eslint-config-love, instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/eslint-config-standard-with-typescript/node_modules/eslint-config-standard": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", @@ -11410,26 +11679,6 @@ "eslint-plugin-promise": "^6.0.0" } }, - "node_modules/eslint-config-standard-with-typescript": { - "version": "23.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-23.0.0.tgz", - "integrity": "sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==", - "deprecated": "Please use eslint-config-love, instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint-config-standard": "17.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0", - "eslint-plugin-promise": "^6.0.0", - "typescript": "*" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -11871,16 +12120,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", + "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -13392,6 +13641,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/getos": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", @@ -14416,9 +14681,9 @@ } }, "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", "dev": true, "license": "MIT" }, @@ -14741,6 +15006,17 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -14768,6 +15044,34 @@ "dev": true, "license": "MIT" }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", @@ -14944,19 +15248,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", @@ -19671,6 +19962,16 @@ "dev": true, "license": "MIT" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/netrc": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/netrc/-/netrc-0.1.4.tgz", @@ -19720,7 +20021,8 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/node-dir": { "version": "0.1.17", @@ -20427,7 +20729,123 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=6" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/pac-proxy-agent/node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" } }, "node_modules/package-json": { @@ -21454,6 +21872,125 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-agent/node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/proxy-agent/node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/proxy-from-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", @@ -21965,6 +22502,14 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -22002,13 +22547,13 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", - "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", "dev": true, "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -22018,14 +22563,14 @@ } }, "node_modules/react-router-dom": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", - "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", "dev": true, "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0", - "react-router": "6.27.0" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" }, "engines": { "node": ">=14.0.0" @@ -23066,15 +23611,14 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.79.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz", - "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==", + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "dev": true, "license": "MIT", "dependencies": { - "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -23082,6 +23626,9 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-loader": { @@ -24081,12 +24628,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/spawn-command": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -25468,6 +26009,26 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.62", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.62.tgz", + "integrity": "sha512-TF+wo3MgTLbf37keEwQD0IxvOZO8UZxnpPJDg5iFGAASGxYzbX/Q0y944ATEjrfxG/pF1TWRHCPbFp49Mz1Y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.62" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.62", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.62.tgz", + "integrity": "sha512-ohONqbfobpuaylhqFbtCzc0dFFeNz85FVKSesgT8DS9OV3a25Yj730pTj7/dDtCqmgoCgEj6gDiU9XxgHKQlBw==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -25921,6 +26482,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true, + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -26617,6 +27185,13 @@ "node": ">=0.10.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -26823,6 +27398,289 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/victory-bar": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-37.3.2.tgz", + "integrity": "sha512-inqb9HLgxheidOAJw7jTMBBR18I7rCgtfH4WuCSMPPtZtUBAEDYFIJzAKzL/LrpC/sWi91fQC2tzmphTD3DS+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2", + "victory-vendor": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-brush-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-37.3.2.tgz", + "integrity": "sha512-SZ4LuM8l3tpGkcnTaGGYREs8gz9E17/c9k3X8uObnXGpz9M3RXkL5hEP0ewtcCjbTr1nwRK3hB4tc1b5BTDj9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-core": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-37.3.2.tgz", + "integrity": "sha512-ez/QW9OGltj0uo9EzsRrGEu/hS49dXoraQKblQJO4PEUkDZclV3Gy3CJ2cRW2qBM3ljTsVITiQvKt3urVj+RDw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-create-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-37.3.2.tgz", + "integrity": "sha512-+hEHeTzeANX1knGOqbeykN1mPUy+zQ8A6LLUSiF8dER+DO2IcF/521LHjBSCRcyjUDik75shfhMc//wryxGBmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-brush-container": "37.3.2", + "victory-core": "37.3.2", + "victory-cursor-container": "37.3.2", + "victory-selection-container": "37.3.2", + "victory-voronoi-container": "37.3.2", + "victory-zoom-container": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-cursor-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-37.3.2.tgz", + "integrity": "sha512-PpBIexoxV/D9S0JTwgI/AmGC5PEjVFbBg+dxbr0iBskXtIckIdXYg11Tejk4iatvUIuvXrJALMjH+y4Jhl+bGw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-group": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-37.3.2.tgz", + "integrity": "sha512-TB0ClboJsCR+ATIwUMgSdkdmau4XUiVaZtc5vehZYB7j50lv3SUtltc4qpztOrilsp1osoKHgUHpj7J8yhRtMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.2", + "victory-shared-events": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-legend": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-37.3.2.tgz", + "integrity": "sha512-NrXnStmdqWtfA2AkwOVVWGwXswN9C9TNnmUKzXY52BMWR4OQfIaeO0etRV3vSrv8yUD/rt+rm0UOAAI7pSb1Tw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-line": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-37.3.2.tgz", + "integrity": "sha512-ZA6A3te2A+egmSPG7jTRu/ZetRE66ggUix11yPaf+7DMZSnRw9f6KXduiUV+fz1otwefJ9pL6vMpXo2SIt9jFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2", + "victory-vendor": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-pie": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-37.3.2.tgz", + "integrity": "sha512-et7Z9d1paoqXdSL8yNpdTadhhhpPgQTrqO5n7vISNJsjOmt0FTLe4/VeKIyugIhERirVkU5Va0CMlMInbNIysw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2", + "victory-vendor": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-scatter": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-37.3.2.tgz", + "integrity": "sha512-mFFIvFC5U+AX1uAgl19HI21L4Mf1rt2YlnjjzPFkJcgy0a2Fv6otjUd4qeqA6pAkVU4e/Dqj8ZZPV2PtfXNeyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-selection-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-37.3.2.tgz", + "integrity": "sha512-g/26Xo4Rxco91GPjZ4jgIQDm95AT4zdG2RqXdQTWEgLbWi+Z/Tk1nodhr1jVjwIjZ6RNMeXcAtcVhFjIPLsWzQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-shared-events": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-37.3.2.tgz", + "integrity": "sha512-L/p/JzbP7O0x/DrWZicVByyiq0YMsDsvc2uinUCoI+XMAXbp7flxC2lbd7YBq41Sg13BHoPBiEoCXH4nKmo8hw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-stack": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-37.3.2.tgz", + "integrity": "sha512-lD8EyzYKTPyWam7h++6VfPxOw4Soj5kYjHqPlbjFjdxoGWYquHrcbHhJ4ra1p+Il9k6iDhy0lIU2DbLa2Ff0wA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.2", + "victory-shared-events": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-tooltip": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-37.3.2.tgz", + "integrity": "sha512-UsEIGY5lqf3pOGr3ikB7oHw+QZtS+Qui/vqLPhoAL1YJ2bQ0WkIhVx2jLTlyCLIrj6aCvyvdePppApFF43E7Zg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.2.tgz", + "integrity": "sha512-2N8j0DIHocffTo2UeZbcd8fcB5+CrQq2KMOSbyTIzYuCVHP10XoS0R/ln7YOU5WoNS6/6L3GEdFWBaGYAAMErQ==", + "dev": true, + "license": "MIT AND ISC", + "peer": true, + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-voronoi-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-37.3.2.tgz", + "integrity": "sha512-Y5NiUcVa6SlAMl5yAI5pRRBRCUCDHJHYeUMCiccTOjA+G2B2pUJkJ5NKeFzMLt7hW1LQv0nnRs9ywpMdViF8nQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "delaunay-find": "0.0.6", + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.2", + "victory-tooltip": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-zoom-container": { + "version": "37.3.2", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-37.3.2.tgz", + "integrity": "sha512-znF3L6+LpQ8hN+7aW8RO+dsHPl1XsMAvf52IKc0OAXZzukIwx1+yW2/OLtHU7G0DzAl3Cxzt5BNd7jFLVXWQJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", @@ -28033,29 +28891,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/module": { "name": "@patternfly/react-data-view", "version": "1.0.0-prerelease.0", "license": "MIT", "dependencies": { - "@patternfly/react-component-groups": "6.0.0-prerelease.7", - "@patternfly/react-core": "6.0.0-prerelease.21", - "@patternfly/react-icons": "6.0.0-prerelease.7", - "@patternfly/react-table": "6.0.0-prerelease.22", + "@patternfly/react-component-groups": "6.0.0", + "@patternfly/react-core": "6.0.0", + "@patternfly/react-icons": "6.0.0", + "@patternfly/react-table": "6.0.0", "clsx": "^2.1.1", "react-jss": "^10.10.0" }, "devDependencies": { - "@patternfly/documentation-framework": "6.0.0-alpha.117", - "@patternfly/patternfly": "6.0.0-prerelease.15", - "@patternfly/patternfly-a11y": "^4.3.1", + "@patternfly/documentation-framework": "6.0.6", + "@patternfly/patternfly": "6.0.0", + "@patternfly/patternfly-a11y": "^5.0.0", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router": "^6.23.0", - "react-router-dom": "^6.23.0", + "react-router-dom": "^6.28.0", "rimraf": "^5.0.5", "typescript": "^5.4.5" }, diff --git a/package.json b/package.json index 4bb6499..e2b0612 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "eslint-config-standard-with-typescript": "^23.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-markdown": "^1.0.2", - "eslint-plugin-n": "^17.13.1", + "eslint-plugin-n": "^15.7.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-promise": "^6.2.0", "eslint-plugin-react": "^7.34.3", diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/Components.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/Components.md index a7efd40..628ecd3 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/Components.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/Components.md @@ -11,7 +11,7 @@ source: react # If you use typescript, the name of the interface to display props for # These are found through the sourceProps function provided in patternfly-docs.source.js sortValue: 4 -propComponents: ['DataViewToolbar', 'DataViewTableBasic', 'DataViewTableTree'] +propComponents: ['DataViewToolbar', 'DataViewTableBasic', 'DataViewTableTree', 'DataViewTrTree', 'DataViewTrObject'] sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/Components.md --- import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter } from '@patternfly/react-core'; @@ -26,7 +26,7 @@ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynami The **data view toolbar** component renders a default opinionated data view toolbar above or below the data section. -Data view toolbar can contain a `pagination`, `bulkSelect`, `actions` or other children content passed. The preffered way of passing children toolbar items is using the [toolbar item](/components/toolbar#toolbar-items) component. +Data view toolbar can contain a `pagination`, `bulkSelect`, `filters`, `actions` or other children content passed. The preffered way of passing children toolbar items is using the [toolbar item](/components/toolbar#toolbar-items) component. ### Basic toolbar example @@ -72,13 +72,15 @@ The `DataViewTable` component accepts the following props: - optional `props` (`TableProps`) that are passed down to the `` component, except for `onSelect`, which is managed internally. +It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component through `selection`. + ### Tree table example This example shows the tree table variant with expandable rows, custom icons for leaf and parent nodes. Tree table is turned on by passing `isTreeTable` flag to the `DataViewTable` component. You can pass `collapsedIcon`, `expandedIcon` or `leafIcon` to be displayen rows with given status. The tree table rows have to be defined in a format of object with following keys: - `row` (`DataViewTd[]`) defining the content for each cell in the row. - `id` (`string`) for the row (used to match items in selection end expand the rows). - optional `children` (`DataViewTrTree[]`) defining the children rows. -It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component. +It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component through `selection`. ```js file="./DataViewTableTreeExample.tsx" diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableExample.tsx index 8805ba9..b9cda39 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableExample.tsx @@ -54,7 +54,7 @@ const rows: DataViewTr[] = repositories.map(({ id, name, branches, prs, workspac const columns: DataViewTh[] = [ null, 'Repositories', - { cell: <>Branches }, + { cell: <>Branches }, 'Pull requests', { cell: 'Workspaces', props: { info: { tooltip: 'More information' } } }, { cell: 'Last commit', props: { sort: { sortBy: {}, columnIndex: 4 } } }, diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md index 2c0cfe4..293a1a0 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md @@ -18,6 +18,7 @@ import { Table, Tbody, Th, Thead, Tr, Td } from '@patternfly/react-table'; import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView'; import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; import { useDataViewEventsContext, DataViewEventsContext, DataViewEventsProvider, EventTypes } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext'; +import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks'; import { Drawer, DrawerContent, DrawerContentBody } from '@patternfly/react-core'; The **data view events context** provides a way of listening to the data view events from the outside of the component. diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx index 19e09be..1447e00 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx @@ -3,6 +3,8 @@ import { Drawer, DrawerActions, DrawerCloseButton, DrawerContent, DrawerContentB import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView'; import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; import { DataViewEventsProvider, EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext'; +import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { ActionsColumn } from '@patternfly/react-table'; interface Repository { name: string; @@ -45,7 +47,7 @@ const RepositoryDetail: React.FunctionComponent = ({ sele return ( - + <Title className="pf-v6-u-mb-md" headingLevel="h2" ouiaId="detail-drawer-title"> Detail of {selectedRepo?.name} Branches: {selectedRepo?.branches} @@ -64,25 +66,45 @@ interface RepositoriesTableProps { selectedRepo?: Repository; } +const rowActions = [ + { + title: 'Some action', + onClick: () => console.log('clicked on Some action') // eslint-disable-line no-console + }, + { + title:
Another action
, + 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: React.FunctionComponent = ({ selectedRepo = undefined }) => { + const selection = useDataViewSelection({ matchOption: (a, b) => a.row[0] === b.row[0] }); const { trigger } = useDataViewEventsContext(); const rows = useMemo(() => { - const handleRowClick = (repo: Repository | undefined) => { - trigger(EventTypes.rowClick, repo); + 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 repositories.map(repo => ({ - row: Object.values(repo), + row: [ ...Object.values(repo), { cell: , props: { isActionCell: true } } ], props: { isClickable: true, - onRowClick: () => handleRowClick(selectedRepo?.name === repo.name ? undefined : repo), + onRowClick: (event) => handleRowClick(event, selectedRepo?.name === repo.name ? undefined : repo), isRowSelected: selectedRepo?.name === repo.name } })); }, [ selectedRepo?.name, trigger ]); return ( - + ); diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx new file mode 100644 index 0000000..afdbcac --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx @@ -0,0 +1,107 @@ +import React, { useMemo } from 'react'; +import { Pagination } from '@patternfly/react-core'; +import { BrowserRouter, useSearchParams } from 'react-router-dom'; +import { useDataViewFilters, useDataViewPagination } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView'; +import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; +import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; +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'; + +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 one', lastCommit: 'Timestamp three' }, + { name: 'Repository four', branch: 'Branch four', prs: 'Pull request four', workspace: 'Workspace one', lastCommit: 'Timestamp four' }, + { name: 'Repository five', branch: 'Branch five', prs: 'Pull request five', workspace: 'Workspace two', lastCommit: 'Timestamp five' }, + { name: 'Repository six', branch: 'Branch six', prs: 'Pull request six', workspace: 'Workspace three', 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 = [ 'Name', 'Branch', 'Pull requests', 'Workspace', 'Last commit' ]; + +const ouiaId = 'LayoutExample'; + +const MyTable: React.FunctionComponent = () => { + const [ searchParams, setSearchParams ] = useSearchParams(); + const { filters, onSetFilters, clearAllFilters } = useDataViewFilters({ initialFilters: { name: '', branch: '', workspace: [] }, searchParams, setSearchParams }); + const pagination = useDataViewPagination({ perPage: 5 }); + const { page, perPage } = pagination; + + const filteredData = useMemo(() => repositories.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 ]); + + const pageRows = useMemo(() => filteredData + .slice((page - 1) * perPage, ((page - 1) * perPage) + perPage) + .map(item => Object.values(item)), + [ page, perPage, filteredData ]); + + return ( + + + } + filters={ + onSetFilters(values)} values={filters}> + + + + + } + /> + + + } + /> + + ); +} + +export const BasicExample: React.FunctionComponent = () => ( + + + +) diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md index c788309..3e263c5 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md @@ -11,16 +11,19 @@ source: react # If you use typescript, the name of the interface to display props for # These are found through the sourceProps function provided in patternfly-docs.source.js sortValue: 3 +propComponents: ['DataViewFilters', 'DataViewTextFilter', 'DataViewCheckboxFilter'] sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md --- import { useMemo } from 'react'; import { BrowserRouter, useSearchParams } from 'react-router-dom'; -import { useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { useDataViewPagination, useDataViewSelection, useDataViewFilters, useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks'; import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView'; import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect'; import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; - +import { 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'; This is a list of functionality you can use to manage data displayed in the **data view**. @@ -83,3 +86,65 @@ The `useDataViewSelection` hook manages the selection state of the data view. ```js file="./SelectionExample.tsx" ``` + +# Filters +Enables filtering of data records in the data view and displays the applied filter labels. + +### Toolbar usage +The data view toolbar can include a set of filters by passing a React node to the `filters` property. You can use predefined components `DataViewFilters`, `DataViewTextFilter` and `DataViewCheckboxFilter` to customize and handle filtering directly in the toolbar. The `DataViewFilters` is a wrapper allowing conditional filtering using multiple attributes. If you need just a single filter, you can use `DataViewTextFilter`, `DataViewCheckboxFilter` or a different filter component alone. Props of these filter components are listed at the bottom of this page. + +You can decide between passing `value` and `onChange` event to every filter separately or pass `values` and `onChange` to the `DataViewFilters` wrapper which make them available to its children. Props directly passed to child filters have a higher priority than the "inherited" ones. + +### Filters state + +The `useDataViewFilters` hook manages the filter state of the data view. It allows you to define default filter values, synchronize filter state with URL parameters, and handle filter changes efficiently. + +**Initial values:** +- `initialFilters` object with default filter values (if the filter param allows multiple values, pass an array) +- optional `searchParams` object for managing URL-based filter state +- optional `setSearchParams` function to update the URL when filters are modified + +The `useDataViewFilters` hook works well with the React Router library to support URL-based filtering. Alternatively, you can manage filter state in the URL using `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If no URL parameters are provided, the filter state is managed internally. + +**Return values:** +- `filters` object representing the current filter values +- `onSetFilters` function to update the filter state +- `clearAllFilters` function to reset all filters to their initial values + +### Filtering example +This example demonstrates the setup and usage of filters within the data view. It includes text filters for different attributes, the ability to clear all filters, and persistence of filter state in the URL. + +```js file="./FiltersExample.tsx" + +``` + +### Sort state + +The `useDataViewSort` hook manages the sorting state of a data view. It provides an easy way to handle sorting logic, including synchronization with URL parameters and defining default sorting behavior. + +**Initial values:** +- `initialSort` object to set default `sortBy` and `direction` values: + - `sortBy`: key of the initial column to sort. + - `direction`: default sorting direction (`asc` or `desc`). +- Optional `searchParams` object to manage URL-based synchronization of sort state. +- Optional `setSearchParams` function to update the URL parameters when sorting changes. +- `defaultDirection` to set the default direction when no direction is specified. +- Customizable parameter names for the URL: + - `sortByParam`: name of the URL parameter for the column key. + - `directionParam`: name of the URL parameter for the sorting direction. + +The `useDataViewSort` hook integrates seamlessly with React Router to manage sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component. + +**Return values:** +- `sortBy`: key of the column currently being sorted. +- `direction`: current sorting direction (`asc` or `desc`). +- `onSort`: function to handle sorting changes programmatically or via user interaction. + +### Sorting example + +This example demonstrates how to set up and use sorting functionality within a data view. The implementation includes dynamic sorting by column with persistence of sort state in the URL using React Router. + + +```js file="./SortingExample.tsx" + +``` diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/SortingExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/SortingExample.tsx new file mode 100644 index 0000000..879f1cd --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Functionality/SortingExample.tsx @@ -0,0 +1,87 @@ +/* eslint-disable no-nested-ternary */ +import React, { useMemo } from 'react'; +import { useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks'; +import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; +import { ThProps } from '@patternfly/react-table'; +import { BrowserRouter, useSearchParams } from 'react-router-dom'; + +interface Repository { + name: string; + branches: string; + prs: string; + workspaces: string; + lastCommit: string; +}; + +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 repositories: Repository[] = [ + { name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: 'Timestamp one' }, + { name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: 'Timestamp two' }, + { name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: 'Timestamp three' }, + { name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: 'Timestamp four' }, + { name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: 'Timestamp five' }, + { name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: 'Timestamp six' } +]; + +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 ouiaId = 'TableExample'; + +export const MyTable: React.FunctionComponent = () => { + const [ searchParams, setSearchParams ] = useSearchParams(); + const { sortBy, direction, onSort } = useDataViewSort({ searchParams, setSearchParams }); + 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 rows: DataViewTr[] = useMemo(() => sortData(repositories, sortBy, direction).map(({ name, branches, prs, workspaces, lastCommit }) => [ + name, + branches, + prs, + workspaces, + lastCommit, + ]), [ sortBy, direction ]); + + return ( + + ); +}; + +export const BasicExample: React.FunctionComponent = () => ( + + + +) + diff --git a/packages/module/patternfly-docs/generated/index.js b/packages/module/patternfly-docs/generated/index.js index 851e24a..278a0b3 100644 --- a/packages/module/patternfly-docs/generated/index.js +++ b/packages/module/patternfly-docs/generated/index.js @@ -49,8 +49,8 @@ module.exports = { '/extensions/data-view/components/react': { id: "Components", title: "Components", - toc: [{"text":"Data view toolbar"},[{"text":"Basic toolbar example"}],{"text":"Data view table"},[{"text":"Rows and columns customization"},{"text":"Tree table example"},{"text":"Empty state example"}]], - examples: ["Basic toolbar example","Rows and columns customization","Tree table example","Empty state example"], + toc: [{"text":"Data view toolbar"},[{"text":"Basic toolbar example"},{"text":"Actions configuration"},{"text":"Actions example"}],{"text":"Data view table"},[{"text":"Rows and columns customization"},{"text":"Tree table example"},{"text":"Empty state example"},{"text":"Error state example"},{"text":"Loading state example"}]], + examples: ["Basic toolbar example","Actions example","Rows and columns customization","Tree table example","Empty state example","Error state example","Loading state example"], section: "extensions", subsection: "Data view", source: "react", diff --git a/packages/module/src/DataView/DataView.tsx b/packages/module/src/DataView/DataView.tsx index 094d594..201a586 100644 --- a/packages/module/src/DataView/DataView.tsx +++ b/packages/module/src/DataView/DataView.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Stack, StackItem } from '@patternfly/react-core'; +import { Stack, StackItem, StackProps } from '@patternfly/react-core'; import { DataViewSelection, InternalContextProvider } from '../InternalContext'; export const DataViewState = { @@ -10,7 +10,8 @@ export const DataViewState = { export type DataViewState = typeof DataViewState[keyof typeof DataViewState]; -export interface DataViewProps { +/** extends StackProps */ +export interface DataViewProps extends StackProps { /** Content rendered inside the data view */ children: React.ReactNode; /** Custom OUIA ID */ diff --git a/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx b/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx new file mode 100644 index 0000000..2290651 --- /dev/null +++ b/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import DataViewCheckboxFilter, { DataViewCheckboxFilterProps } from './DataViewCheckboxFilter'; +import DataViewToolbar from '../DataViewToolbar'; + +describe('DataViewCheckboxFilter component', () => { + const defaultProps: DataViewCheckboxFilterProps = { + filterId: 'test-checkbox-filter', + title: 'Test Checkbox Filter', + value: [ 'workspace-one' ], + options: [ + { label: 'Workspace one', value: 'workspace-one' }, + { label: 'Workspace two', value: 'workspace-two' }, + { label: 'Workspace three', value: 'workspace-three' }, + ], + }; + + it('should render correctly', () => { + const { container } = render( + } /> + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx b/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx new file mode 100644 index 0000000..e52ff7e --- /dev/null +++ b/packages/module/src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx @@ -0,0 +1,175 @@ +import React from 'react'; +import { + Badge, + Menu, + MenuContent, + MenuItem, + MenuList, + MenuProps, + MenuToggle, + Popper, + ToolbarLabel, + ToolbarFilter, +} from '@patternfly/react-core'; +import { FilterIcon } from '@patternfly/react-icons'; +import { DataViewFilterOption } from '../DataViewFilters'; + +const isToolbarLabel = (label: string | ToolbarLabel): label is ToolbarLabel => + typeof label === 'object' && 'key' in label; + +export const isDataViewFilterOption = (obj: unknown): obj is DataViewFilterOption => + !!obj && + typeof obj === 'object' && + 'label' in obj && + 'value' in obj && + typeof (obj as DataViewFilterOption).value === 'string'; + +/** extends MenuProps */ +export interface DataViewCheckboxFilterProps extends Omit { + /** Unique key for the filter attribute */ + filterId: string; + /** Array of current filter values */ + value?: string[]; + /** Filter title displayed in the toolbar */ + title: string; + /** Placeholder text of the menu */ + placeholder?: string; + /** Filter options displayed */ + options: (DataViewFilterOption | string)[]; + /** Callback for updating when item selection changes. */ + onChange?: (event?: React.MouseEvent, values?: string[]) => void; + /** Controls visibility of the filter in the toolbar */ + showToolbarItem?: boolean; + /** Controls visibility of the filter icon */ + showIcon?: boolean; + /** Controls visibility of the selected items badge */ + showBadge?: boolean; + /** Custom OUIA ID */ + ouiaId?: string; +} + +export const DataViewCheckboxFilter: React.FC = ({ + filterId, + title, + value = [], + onChange, + placeholder, + options = [], + showToolbarItem, + showIcon = !placeholder, + showBadge = !placeholder, + ouiaId = 'DataViewCheckboxFilter', + ...props +}: DataViewCheckboxFilterProps) => { + const [ isOpen, setIsOpen ] = React.useState(false); + const toggleRef = React.useRef(null); + const menuRef = React.useRef(null); + const containerRef = React.useRef(null); + + const normalizeOptions = React.useMemo( + () => + options.map(option => + typeof option === 'string' + ? { label: option, value: option } + : option + ), + [ options ] + ); + + const handleToggleClick = (event: React.MouseEvent) => { + event.stopPropagation(); + setTimeout(() => { + const firstElement = menuRef.current?.querySelector('li > button:not(:disabled)') as HTMLElement; + firstElement?.focus(); + }, 0); + setIsOpen(prev => !prev); + }; + + const handleSelect = (event?: React.MouseEvent, itemId?: string | number) => { + const activeItem = String(itemId); + const isSelected = value.includes(activeItem); + + onChange?.( + event, + isSelected ? value.filter(item => item !== activeItem) : [ activeItem, ...value ] + ); + }; + + const handleClickOutside = (event: MouseEvent) => + isOpen && + menuRef.current && toggleRef.current && + !menuRef.current.contains(event.target as Node) && !toggleRef.current.contains(event.target as Node) + && setIsOpen(false); + + + React.useEffect(() => { + window.addEventListener('click', handleClickOutside); + return () => { + window.removeEventListener('click', handleClickOutside); + }; + }, [ isOpen ]); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + { + const activeOption = normalizeOptions.find(option => option.value === item); + return ({ key: activeOption?.value as string, node: activeOption?.label }) + })} + deleteLabel={(_, label) => + onChange?.(undefined, value.filter(item => item !== (isToolbarLabel(label) ? label.key : label))) + } + categoryName={title} + showToolbarItem={showToolbarItem} + > + : undefined} + badge={value.length > 0 && showBadge ? {value.length} : undefined} + style={{ width: '200px' }} + > + {placeholder ?? title} + + } + triggerRef={toggleRef} + popper={ + + + + {normalizeOptions.map(option => ( + + {option.label} + + ))} + + + + } + popperRef={menuRef} + appendTo={containerRef.current || undefined} + aria-label={`${title ?? filterId} filter`} + isVisible={isOpen} + /> + + ); +}; + +export default DataViewCheckboxFilter; diff --git a/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap b/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap new file mode 100644 index 0000000..1087ae1 --- /dev/null +++ b/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap @@ -0,0 +1,197 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataViewCheckboxFilter component should render correctly 1`] = ` +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
    +
  • + + + + Workspace one + + + + + + +
  • +
+
+
+
+
+
+
+ +
+
+
+
+
+`; diff --git a/packages/module/src/DataViewCheckboxFilter/index.ts b/packages/module/src/DataViewCheckboxFilter/index.ts new file mode 100644 index 0000000..218b16f --- /dev/null +++ b/packages/module/src/DataViewCheckboxFilter/index.ts @@ -0,0 +1,2 @@ +export { default } from './DataViewCheckboxFilter'; +export * from './DataViewCheckboxFilter'; diff --git a/packages/module/src/DataViewFilters/DataViewFilters.test.tsx b/packages/module/src/DataViewFilters/DataViewFilters.test.tsx new file mode 100644 index 0000000..9f9cb5a --- /dev/null +++ b/packages/module/src/DataViewFilters/DataViewFilters.test.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import DataViewFilters from './DataViewFilters'; +import DataViewToolbar from '../DataViewToolbar'; +import DataViewTextFilter from '../DataViewTextFilter'; + +describe('DataViewFilters component', () => { + const mockOnChange = jest.fn(); + + it('should render correctly', () => { + const { container } = render( + + + + } + />); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/module/src/DataViewFilters/DataViewFilters.tsx b/packages/module/src/DataViewFilters/DataViewFilters.tsx new file mode 100644 index 0000000..addc63f --- /dev/null +++ b/packages/module/src/DataViewFilters/DataViewFilters.tsx @@ -0,0 +1,144 @@ +import React, { useMemo, useState, useRef, useEffect, ReactElement, ReactNode } from 'react'; +import { + Menu, MenuContent, MenuItem, MenuList, MenuToggle, Popper, ToolbarGroup, ToolbarToggleGroup, ToolbarToggleGroupProps, +} from '@patternfly/react-core'; +import { FilterIcon } from '@patternfly/react-icons'; + +export interface DataViewFilterOption { + /** Filter option label */ + label: ReactNode; + /** Filter option value */ + value: string; +} + +// helper interface to generate attribute menu +interface DataViewFilterIdentifiers { + filterId: string; + title: string; +} + +/** extends ToolbarToggleGroupProps */ +export interface DataViewFiltersProps extends Omit { + /** Content rendered inside the data view */ + children: React.ReactNode; + /** Optional onChange callback shared across filters */ + onChange?: (key: string, newValues: Partial) => void; + /** Optional values shared across filters */ + values?: T; + /** Icon for the toolbar toggle group */ + toggleIcon?: ToolbarToggleGroupProps['toggleIcon']; + /** Breakpoint for the toolbar toggle group */ + breakpoint?: ToolbarToggleGroupProps['breakpoint']; + /** Custom OUIA ID */ + ouiaId?: string; +}; + + +export const DataViewFilters = ({ + children, + ouiaId = 'DataViewFilters', + toggleIcon = , + breakpoint = 'xl', + onChange, + values, + ...props +}: DataViewFiltersProps) => { + const [ activeAttributeMenu, setActiveAttributeMenu ] = useState(''); + const [ isAttributeMenuOpen, setIsAttributeMenuOpen ] = useState(false); + const attributeToggleRef = useRef(null); + const attributeMenuRef = useRef(null); + const attributeContainerRef = useRef(null); + + const childrenHash = useMemo(() => JSON.stringify( + React.Children.map(children, (child) => + React.isValidElement(child) ? { type: child.type, key: child.key, props: child.props } : child + ) + ), [ children ]); + + const filterItems: DataViewFilterIdentifiers[] = useMemo(() => React.Children.toArray(children) + .map(child => + React.isValidElement(child) ? { filterId: String(child.props.filterId), title: String(child.props.title) } : undefined + ).filter((item): item is DataViewFilterIdentifiers => !!item), [ childrenHash ]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + filterItems.length > 0 && setActiveAttributeMenu(filterItems[0].title); + }, [ filterItems ]); + + const handleClickOutside = (event: MouseEvent) => + isAttributeMenuOpen && + !attributeMenuRef.current?.contains(event.target as Node) && + !attributeToggleRef.current?.contains(event.target as Node) + && setIsAttributeMenuOpen(false); + + useEffect(() => { + window.addEventListener('click', handleClickOutside); + return () => { + window.removeEventListener('click', handleClickOutside); + }; + }, [ isAttributeMenuOpen ]); // eslint-disable-line react-hooks/exhaustive-deps + + const attributeToggle = ( + setIsAttributeMenuOpen(!isAttributeMenuOpen)} + isExpanded={isAttributeMenuOpen} + icon={toggleIcon} + > + {activeAttributeMenu} + + ); + + const attributeMenu = ( + { + const selectedItem = filterItems.find(item => item.filterId === itemId); + selectedItem && setActiveAttributeMenu(selectedItem.title); + setIsAttributeMenuOpen(false); + }} + > + + + {filterItems.map(item => ( + + {item.title} + + ))} + + + + ); + + return ( + + +
+ +
+ {React.Children.map(children, (child) => + React.isValidElement(child) + ? React.cloneElement(child as ReactElement<{ + showToolbarItem: boolean; + onChange: (_e: unknown, values: unknown) => void; + value: unknown; + }>, { + showToolbarItem: activeAttributeMenu === child.props.title, + onChange: (event, value) => onChange?.(event, { [child.props.filterId]: value } as Partial), + value: values?.[child.props.filterId], + ...child.props + }) + : child + )} +
+
+ ); +}; + +export default DataViewFilters; diff --git a/packages/module/src/DataViewFilters/__snapshots__/DataViewFilters.test.tsx.snap b/packages/module/src/DataViewFilters/__snapshots__/DataViewFilters.test.tsx.snap new file mode 100644 index 0000000..67a9cec --- /dev/null +++ b/packages/module/src/DataViewFilters/__snapshots__/DataViewFilters.test.tsx.snap @@ -0,0 +1,194 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataViewFilters component should render correctly 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+ + + + + + +
+
+
+
+
+
+
+
+ +
+`; diff --git a/packages/module/src/DataViewFilters/index.tsx b/packages/module/src/DataViewFilters/index.tsx new file mode 100644 index 0000000..ba0ff37 --- /dev/null +++ b/packages/module/src/DataViewFilters/index.tsx @@ -0,0 +1,2 @@ +export { default } from './DataViewFilters'; +export * from './DataViewFilters'; diff --git a/packages/module/src/DataViewTable/DataViewTable.tsx b/packages/module/src/DataViewTable/DataViewTable.tsx index f7a0697..97a4260 100644 --- a/packages/module/src/DataViewTable/DataViewTable.tsx +++ b/packages/module/src/DataViewTable/DataViewTable.tsx @@ -8,18 +8,38 @@ import { DataViewTableTree, DataViewTableTreeProps } from '../DataViewTableTree' import { DataViewTableBasic, DataViewTableBasicProps } from '../DataViewTableBasic'; // Table head typings -export type DataViewTh = ReactNode | { cell: ReactNode; props?: ThProps }; +export type DataViewTh = ReactNode | { + /** Table head cell node */ + cell: ReactNode; + /** Props passed to Th */ + props?: ThProps +}; export const isDataViewThObject = (value: DataViewTh): value is { cell: ReactNode; props?: ThProps } => value != null && typeof value === 'object' && 'cell' in value; // Basic table typings -export interface DataViewTrObject { row: DataViewTd[], id?: string, props?: TrProps } -export type DataViewTd = ReactNode | { cell: ReactNode; props?: TdProps }; +export interface DataViewTrObject { + /** Array of rows */ + row: DataViewTd[], + /** Unique identifier of a row */ + id?: string, + /** Props passed to Tr */ + props?: TrProps +} + +export type DataViewTd = ReactNode | { + /** Table body cell node */ + cell: ReactNode; + /** Props passed to Td */ + props?: TdProps +}; + export type DataViewTr = DataViewTd[] | DataViewTrObject; export const isDataViewTdObject = (value: DataViewTd): value is { cell: ReactNode; props?: TdProps } => value != null && typeof value === 'object' && 'cell' in value; export const isDataViewTrObject = (value: DataViewTr): value is { row: DataViewTd[], id?: string } => value != null && typeof value === 'object' && 'row' in value; // Tree table typings +/** extends DataViewTrObject */ export interface DataViewTrTree extends DataViewTrObject { id: string, children?: DataViewTrTree[] } export type DataViewTableProps = diff --git a/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx b/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx index d2f1ac8..2b32cae 100644 --- a/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx +++ b/packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx @@ -11,6 +11,7 @@ import { DataViewTableHead } from '../DataViewTableHead'; import { DataViewTh, DataViewTr, isDataViewTdObject, isDataViewTrObject } from '../DataViewTable'; import { DataViewState } from '../DataView/DataView'; +/** extends TableProps */ export interface DataViewTableBasicProps extends Omit { /** Columns definition */ columns: DataViewTh[]; diff --git a/packages/module/src/DataViewTableHead/DataViewTableHead.tsx b/packages/module/src/DataViewTableHead/DataViewTableHead.tsx index 8466389..d1073d4 100644 --- a/packages/module/src/DataViewTableHead/DataViewTableHead.tsx +++ b/packages/module/src/DataViewTableHead/DataViewTableHead.tsx @@ -8,6 +8,7 @@ import { import { useInternalContext } from '../InternalContext'; import { DataViewTh, isDataViewThObject } from '../DataViewTable'; +/** extends TheadProps */ export interface DataViewTableHeadProps extends TheadProps { /** Indicates whether table is a tree */ isTreeTable?: boolean; diff --git a/packages/module/src/DataViewTableTree/DataViewTableTree.tsx b/packages/module/src/DataViewTableTree/DataViewTableTree.tsx index a5643f8..d4aaab8 100644 --- a/packages/module/src/DataViewTableTree/DataViewTableTree.tsx +++ b/packages/module/src/DataViewTableTree/DataViewTableTree.tsx @@ -12,24 +12,46 @@ import { DataViewTableHead } from '../DataViewTableHead'; import { DataViewTh, DataViewTrTree, isDataViewTdObject } from '../DataViewTable'; import { DataViewState } from '../DataView/DataView'; -const getDescendants = (node: DataViewTrTree): DataViewTrTree[] => (!node.children || !node.children.length) ? [ node ] : node.children.flatMap(getDescendants); - -const isNodeChecked = (node: DataViewTrTree, isSelected: (node: DataViewTrTree) => boolean) => { - let allSelected = true; - let someSelected = false; - - for (const descendant of getDescendants(node)) { - const selected = !!isSelected?.(descendant); - - someSelected ||= selected; - allSelected &&= selected; - - if (!allSelected && someSelected) { return null } - } - - return allSelected; +const getNodesAffectedBySelection = ( + allRows: DataViewTrTree[], + node: DataViewTrTree, + isChecking: boolean, + isSelected?: (item: DataViewTrTree) => boolean +): DataViewTrTree[] => { + + const getDescendants = (node: DataViewTrTree): DataViewTrTree[] => + node.children ? node.children.flatMap(getDescendants).concat(node) : [ node ]; + + const findParent = (child: DataViewTrTree, rows: DataViewTrTree[]): DataViewTrTree | undefined => + rows.find(row => row.children?.some(c => c === child)) ?? + rows.flatMap(row => row.children ?? []).map(c => findParent(child, [ c ])).find(p => p); + + const getAncestors = (node: DataViewTrTree): DataViewTrTree[] => { + const ancestors: DataViewTrTree[] = []; + let parent = findParent(node, allRows); + while (parent) { + ancestors.push(parent); + parent = findParent(parent, allRows); + } + return ancestors; + }; + + const affectedNodes = new Set([ node, ...getDescendants(node) ]); + + getAncestors(node).forEach(ancestor => { + const allChildrenSelected = ancestor.children?.every(child => isSelected?.(child) || affectedNodes.has(child)); + const anyChildAffected = ancestor.children?.some(child => affectedNodes.has(child) || child.id === node.id); + + if (isChecking ? !isSelected?.(ancestor) && allChildrenSelected : isSelected?.(ancestor) && anyChildAffected) { + affectedNodes.add(ancestor); + } + }); + + return Array.from(affectedNodes); }; + +/** extends TableProps */ export interface DataViewTableTreeProps extends Omit { /** Columns definition */ columns: DataViewTh[]; @@ -82,7 +104,7 @@ export const DataViewTableTree: React.FC = ({ } const isExpanded = expandedNodeIds.includes(node.id); const isDetailsExpanded = expandedDetailsNodeNames.includes(node.id); - const isChecked = isSelected && isNodeChecked(node, isSelected); + const isChecked = isSelected?.(node); let icon = leafIcon; if (node.children) { icon = isExpanded ? expandedIcon : collapsedIcon; @@ -99,7 +121,7 @@ export const DataViewTableTree: React.FC = ({ const otherDetailsExpandedNodeIds = prevDetailsExpanded.filter(id => id !== node.id); return isDetailsExpanded ? otherDetailsExpandedNodeIds : [ ...otherDetailsExpandedNodeIds, node.id ]; }), - onCheckChange: (isSelectDisabled?.(node) || !onSelect) ? undefined : (_event, isChecking) => onSelect?.(isChecking, getDescendants(node)), + onCheckChange: (isSelectDisabled?.(node) || !onSelect) ? undefined : (_event, isChecking) => onSelect?.(isChecking, getNodesAffectedBySelection(rows, node, isChecking, isSelected)), rowIndex, props: { isExpanded, diff --git a/packages/module/src/DataViewTextFilter/DataViewTextFilter.test.tsx b/packages/module/src/DataViewTextFilter/DataViewTextFilter.test.tsx new file mode 100644 index 0000000..5630735 --- /dev/null +++ b/packages/module/src/DataViewTextFilter/DataViewTextFilter.test.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import DataViewTextFilter, { DataViewTextFilterProps } from './DataViewTextFilter'; +import DataViewToolbar from '../DataViewToolbar'; + +describe('DataViewTextFilter component', () => { + const mockOnChange = jest.fn(); + + const defaultProps: DataViewTextFilterProps = { + filterId: 'test-filter', + title: 'Test Filter', + value: 'initial value', + onChange: mockOnChange, + }; + + it('should render correctly', () => { + const { container } = render( + } + />); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/module/src/DataViewTextFilter/DataViewTextFilter.tsx b/packages/module/src/DataViewTextFilter/DataViewTextFilter.tsx new file mode 100644 index 0000000..9b4ff34 --- /dev/null +++ b/packages/module/src/DataViewTextFilter/DataViewTextFilter.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { SearchInput, SearchInputProps, ToolbarFilter, ToolbarFilterProps } from '@patternfly/react-core'; + +/** extends SearchInputProps */ +export interface DataViewTextFilterProps extends SearchInputProps { + /** Unique key for the filter attribute */ + filterId: string; + /** Current filter value */ + value?: string; + /** Filter title displayed in the toolbar */ + title: string; + /** Callback for when the input value changes */ + onChange?: (event: React.FormEvent | undefined, value: string) => void; + /** Controls visibility of the filter in the toolbar */ + showToolbarItem?: ToolbarFilterProps['showToolbarItem']; + /** Trims input value on change */ + trimValue?: boolean; + /** Custom OUIA ID */ + ouiaId?: string; +} + +export const DataViewTextFilter: React.FC = ({ + filterId, + title, + value = '', + onChange, + onClear = () => onChange?.(undefined, ''), + showToolbarItem, + trimValue = true, + ouiaId = 'DataViewTextFilter', + ...props +}: DataViewTextFilterProps) => ( + 0 ? [ { key: title, node: value } ] : []} + deleteLabel={() => onChange?.(undefined, '')} + categoryName={title} + showToolbarItem={showToolbarItem} + > + onChange?.(e, trimValue ? inputValue.trim() : inputValue)} + onClear={onClear} + placeholder={`Filter by ${title}`} + aria-label={`${title ?? filterId} filter`} + data-ouia-component-id={`${ouiaId}-input`} + {...props} + /> + +); + +export default DataViewTextFilter; diff --git a/packages/module/src/DataViewTextFilter/__snapshots__/DataViewTextFilter.test.tsx.snap b/packages/module/src/DataViewTextFilter/__snapshots__/DataViewTextFilter.test.tsx.snap new file mode 100644 index 0000000..1d8a389 --- /dev/null +++ b/packages/module/src/DataViewTextFilter/__snapshots__/DataViewTextFilter.test.tsx.snap @@ -0,0 +1,203 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataViewTextFilter component should render correctly 1`] = ` +
+
+
+
+
+
+
+
+ + + + + + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
    +
  • + + + + initial value + + + + + + +
  • +
+
+
+
+
+
+
+ +
+
+
+
+
+`; diff --git a/packages/module/src/DataViewTextFilter/index.ts b/packages/module/src/DataViewTextFilter/index.ts new file mode 100644 index 0000000..fd2e65a --- /dev/null +++ b/packages/module/src/DataViewTextFilter/index.ts @@ -0,0 +1,2 @@ +export { default } from './DataViewTextFilter'; +export * from './DataViewTextFilter'; diff --git a/packages/module/src/DataViewToolbar/DataViewToolbar.tsx b/packages/module/src/DataViewToolbar/DataViewToolbar.tsx index c2f6f1d..1258528 100644 --- a/packages/module/src/DataViewToolbar/DataViewToolbar.tsx +++ b/packages/module/src/DataViewToolbar/DataViewToolbar.tsx @@ -1,41 +1,60 @@ -import React, { PropsWithChildren } from 'react'; -import { Toolbar, ToolbarContent, ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; +import React, { PropsWithChildren, useRef } from 'react'; +import { Button, Toolbar, ToolbarContent, ToolbarItem, ToolbarItemVariant, ToolbarProps } from '@patternfly/react-core'; -export interface DataViewToolbarProps extends PropsWithChildren { +/** extends ToolbarProps */ +export interface DataViewToolbarProps extends Omit, 'ref'> { /** Toolbar className */ className?: string; /** Custom OUIA ID */ ouiaId?: string; - /** React component to display bulk select */ + /** React node to display bulk select */ bulkSelect?: React.ReactNode; - /** React component to display pagination */ + /** React node to display pagination */ pagination?: React.ReactNode; - /** React component to display actions */ + /** React node to display actions */ actions?: React.ReactNode; + /** React node to display filters */ + filters?: React.ReactNode; + /** React node to display custom filter labels */ + customLabelGroupContent?: React.ReactNode; } -export const DataViewToolbar: React.FC = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, actions, pagination, children, ...props }: DataViewToolbarProps) => ( - - - {bulkSelect && ( - - {bulkSelect} - - )} - {actions && ( - - {actions} - - )} - {pagination && ( - - {pagination} - - )} - {children} - - -) +export const DataViewToolbar: React.FC = ({ className, ouiaId = 'DataViewToolbar', bulkSelect, actions, pagination, filters, customLabelGroupContent, clearAllFilters, children, ...props }: DataViewToolbarProps) => { + const defaultClearFilters = useRef( + + + + ); + return ( + + + {bulkSelect && ( + + {bulkSelect} + + )} + {actions && ( + + {actions} + + )} + {filters && ( + + {filters} + + )} + {pagination && ( + + {pagination} + + )} + {children} + + + ) +}; export default DataViewToolbar; diff --git a/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap b/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap index 0573355..c7fef59 100644 --- a/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap +++ b/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap @@ -263,6 +263,28 @@ exports[`DataViewToolbar component should render correctly 1`] = `
+
+
+ +
+
@@ -526,6 +548,28 @@ exports[`DataViewToolbar component should render correctly 1`] = `
+
+
+ +
+
, diff --git a/packages/module/src/Hooks/filters.test.tsx b/packages/module/src/Hooks/filters.test.tsx new file mode 100644 index 0000000..d3ea7a8 --- /dev/null +++ b/packages/module/src/Hooks/filters.test.tsx @@ -0,0 +1,62 @@ +import '@testing-library/jest-dom'; +import { renderHook, act } from '@testing-library/react'; +import { useDataViewFilters, UseDataViewFiltersProps } from './filters'; + +describe('useDataViewFilters', () => { + const initialFilters = { search: 'test', tags: [ 'tag1', 'tag2' ] }; + + it('should initialize with provided initial filters', () => { + const { result } = renderHook(() => useDataViewFilters({ initialFilters })); + expect(result.current.filters).toEqual(initialFilters); + }); + + it('should initialize with empty filters if no initialFilters provided', () => { + const { result } = renderHook(() => useDataViewFilters({})); + expect(result.current.filters).toEqual({}); + }); + + it('should set filters correctly', () => { + const { result } = renderHook(() => useDataViewFilters({ initialFilters })); + const newFilters = { search: 'new search' }; + act(() => result.current.onSetFilters(newFilters)); + + expect(result.current.filters).toEqual({ ...initialFilters, ...newFilters }); + }); + + it('should delete specific filters without removing keys', () => { + const { result } = renderHook(() => useDataViewFilters({ initialFilters })); + const filtersToDelete = { search: 'test' }; + act(() => result.current.onDeleteFilters(filtersToDelete)); + + expect(result.current.filters).toEqual({ search: '', tags: [ 'tag1', 'tag2' ] }); + }); + + it('should clear all filters', () => { + const { result } = renderHook(() => useDataViewFilters({ initialFilters })); + act(() => result.current.clearAllFilters()); + + expect(result.current.filters).toEqual({ search: '', tags: [] }); + }); + + it('should sync with URL search params if isUrlSyncEnabled', () => { + const searchParams = new URLSearchParams(); + const setSearchParams = jest.fn(); + const props: UseDataViewFiltersProps = { + initialFilters, + searchParams, + setSearchParams, + }; + + const { result } = renderHook(() => useDataViewFilters(props)); + act(() => result.current.onSetFilters({ search: 'new search' })); + + expect(setSearchParams).toHaveBeenCalled(); + }); + + it('should reset filters to default values when clearAllFilters is called', () => { + const { result } = renderHook(() => useDataViewFilters({ initialFilters })); + act(() => result.current.clearAllFilters()); + + expect(result.current.filters).toEqual({ search: '', tags: [] }); + }); +}); diff --git a/packages/module/src/Hooks/filters.ts b/packages/module/src/Hooks/filters.ts new file mode 100644 index 0000000..177e5f3 --- /dev/null +++ b/packages/module/src/Hooks/filters.ts @@ -0,0 +1,97 @@ +import { useState, useCallback, useEffect, useMemo } from "react"; + +export interface UseDataViewFiltersProps { + /** Initial filters object */ + initialFilters?: T; + /** Current search parameters as a string */ + searchParams?: URLSearchParams; + /** Function to set search parameters */ + setSearchParams?: (params: URLSearchParams) => void; +}; + +export const useDataViewFilters = ({ + initialFilters = {} as T, + searchParams, + setSearchParams, +}: UseDataViewFiltersProps) => { + const isUrlSyncEnabled = useMemo(() => searchParams && !!setSearchParams, [ searchParams, setSearchParams ]); + + const getInitialFilters = useCallback((): T => isUrlSyncEnabled + ? Object.keys(initialFilters).reduce((loadedFilters, key) => { + const urlValue = searchParams?.get(key); + const isArrayFilter = Array.isArray(initialFilters[key]); + + // eslint-disable-next-line no-nested-ternary + loadedFilters[key] = urlValue + ? (isArrayFilter && !Array.isArray(urlValue) ? [ urlValue ] : urlValue) + : initialFilters[key]; + + return loadedFilters; + }, { ...initialFilters }) + : initialFilters, [ isUrlSyncEnabled, initialFilters, searchParams ]); + const [ filters, setFilters ] = useState(getInitialFilters()); + + const updateSearchParams = useCallback( + (newFilters: T) => { + if (isUrlSyncEnabled) { + const params = new URLSearchParams(searchParams); + Object.entries(newFilters).forEach(([ key, value ]) => { + params.delete(key); + (Array.isArray(value) ? value : [ value ]).forEach((val) => value && params.append(key, val)); + }); + setSearchParams?.(params); + } + }, + [ isUrlSyncEnabled, searchParams, setSearchParams ] + ); + + useEffect(() => { + isUrlSyncEnabled && setFilters(getInitialFilters()) + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const onSetFilters = useCallback( + (newFilters: Partial) => { + setFilters(prevFilters => { + const updatedFilters = { ...prevFilters, ...newFilters }; + isUrlSyncEnabled && updateSearchParams(updatedFilters); + return updatedFilters; + }); + }, + [ isUrlSyncEnabled, updateSearchParams ] + ); + + // helper function to reset filters + const resetFilterValues = useCallback((filters: Partial): Partial => Object.entries(filters).reduce((acc, [ key, value ]) => { + if (Array.isArray(value)) { + acc[key as keyof T] = [] as T[keyof T]; + } else { + acc[key as keyof T] = '' as T[keyof T]; + } + return acc; + }, {} as Partial), []); + + const onDeleteFilters = useCallback( + (filtersToDelete: Partial) => { + setFilters(prevFilters => { + const updatedFilters = { ...prevFilters,...resetFilterValues(filtersToDelete) }; + isUrlSyncEnabled && updateSearchParams(updatedFilters); + return updatedFilters; + }); + }, + [ isUrlSyncEnabled, updateSearchParams, resetFilterValues ] + ); + + const clearAllFilters = useCallback(() => { + const clearedFilters = resetFilterValues(filters) as T; + + setFilters(clearedFilters); + isUrlSyncEnabled && updateSearchParams(clearedFilters); + }, [ filters, isUrlSyncEnabled, updateSearchParams, resetFilterValues ]); + + return { + filters, + onSetFilters, + onDeleteFilters, + clearAllFilters, + }; +}; diff --git a/packages/module/src/Hooks/index.ts b/packages/module/src/Hooks/index.ts index 4ed29ea..3ce609c 100644 --- a/packages/module/src/Hooks/index.ts +++ b/packages/module/src/Hooks/index.ts @@ -1,2 +1,4 @@ export * from './pagination'; export * from './selection'; +export * from './filters'; +export * from './sort'; diff --git a/packages/module/src/Hooks/pagination.ts b/packages/module/src/Hooks/pagination.ts index 9f48c52..4209f66 100644 --- a/packages/module/src/Hooks/pagination.ts +++ b/packages/module/src/Hooks/pagination.ts @@ -15,6 +15,7 @@ export interface UseDataViewPaginationProps { perPageParam?: string; } +/** extends UseDataViewPaginationProps */ export interface DataViewPaginationProps extends UseDataViewPaginationProps { /** Current page number */ page: number; diff --git a/packages/module/src/Hooks/selection.ts b/packages/module/src/Hooks/selection.ts index d5858ae..2ba443f 100644 --- a/packages/module/src/Hooks/selection.ts +++ b/packages/module/src/Hooks/selection.ts @@ -8,8 +8,9 @@ export interface UseDataViewSelectionProps { initialSelected?: (any)[]; } -export const useDataViewSelection = ({ matchOption, initialSelected = [] }: UseDataViewSelectionProps) => { - const [ selected, setSelected ] = useState(initialSelected); +export const useDataViewSelection = (props?: UseDataViewSelectionProps) => { + const [ selected, setSelected ] = useState(props?.initialSelected ?? []); + const matchOption = props?.matchOption ? props.matchOption : (option, another) => (option === another); const onSelect = (isSelecting: boolean, items?: any[] | any) => { isSelecting && items ? diff --git a/packages/module/src/Hooks/sort.test.tsx b/packages/module/src/Hooks/sort.test.tsx new file mode 100644 index 0000000..473924d --- /dev/null +++ b/packages/module/src/Hooks/sort.test.tsx @@ -0,0 +1,84 @@ +import '@testing-library/jest-dom'; +import { renderHook, act } from '@testing-library/react'; +import { useDataViewSort, UseDataViewSortProps, DataViewSortConfig, DataViewSortParams } from './sort'; + +describe('useDataViewSort', () => { + const initialSort: DataViewSortConfig = { sortBy: 'name', direction: 'asc' }; + + it('should initialize with provided initial sort config', () => { + const { result } = renderHook(() => useDataViewSort({ initialSort })); + expect(result.current).toEqual(expect.objectContaining(initialSort)); + }); + + it('should initialize with empty sort config if no initialSort is provided', () => { + const { result } = renderHook(() => useDataViewSort()); + expect(result.current).toEqual(expect.objectContaining({ sortBy: undefined, direction: 'asc' })); + }); + + it('should update sort state when onSort is called', () => { + const { result } = renderHook(() => useDataViewSort({ initialSort })); + act(() => { + result.current.onSort(undefined, 'age', 'desc'); + }); + expect(result.current).toEqual(expect.objectContaining({ sortBy: 'age', direction: 'desc' })); + }); + + it('should sync with URL search params if isUrlSyncEnabled', () => { + const searchParams = new URLSearchParams(); + const setSearchParams = jest.fn(); + const props: UseDataViewSortProps = { + initialSort, + searchParams, + setSearchParams, + }; + + const { result } = renderHook(() => useDataViewSort(props)); + + expect(setSearchParams).toHaveBeenCalledTimes(1); + expect(result.current).toEqual(expect.objectContaining(initialSort)); + }); + + it('should validate direction and fallback to default direction if invalid direction is provided', () => { + const searchParams = new URLSearchParams(); + searchParams.set(DataViewSortParams.SORT_BY, 'name'); + searchParams.set(DataViewSortParams.DIRECTION, 'invalid-direction'); + const { result } = renderHook(() => useDataViewSort({ searchParams, defaultDirection: 'desc' })); + + expect(result.current).toEqual(expect.objectContaining({ sortBy: 'name', direction: 'desc' })); + }); + + it('should update search params when URL sync is enabled and sort changes', () => { + const searchParams = new URLSearchParams(); + const setSearchParams = jest.fn(); + const props: UseDataViewSortProps = { + initialSort, + searchParams, + setSearchParams, + }; + + const { result } = renderHook(() => useDataViewSort(props)); + act(() => { + expect(setSearchParams).toHaveBeenCalledTimes(1); + result.current.onSort(undefined, 'priority', 'desc'); + }); + + expect(setSearchParams).toHaveBeenCalledTimes(2); + expect(result.current).toEqual(expect.objectContaining({ sortBy: 'priority', direction: 'desc' })); + }); + + it('should prioritize searchParams values', () => { + const searchParams = new URLSearchParams(); + searchParams.set(DataViewSortParams.SORT_BY, 'category'); + searchParams.set(DataViewSortParams.DIRECTION, 'desc'); + + const { result } = renderHook( + (props: UseDataViewSortProps) => useDataViewSort(props), + { initialProps: { initialSort, searchParams } } + ); + + expect(result.current).toEqual(expect.objectContaining({ + sortBy: 'category', + direction: 'desc', + })); + }); +}); diff --git a/packages/module/src/Hooks/sort.ts b/packages/module/src/Hooks/sort.ts new file mode 100644 index 0000000..ed06f22 --- /dev/null +++ b/packages/module/src/Hooks/sort.ts @@ -0,0 +1,87 @@ +import { ISortBy } from "@patternfly/react-table"; +import { useState, useEffect, useMemo } from "react"; + +export enum DataViewSortParams { + SORT_BY = 'sortBy', + DIRECTION = 'direction' +}; + +const validateDirection = (direction: string | null | undefined, defaultDirection: ISortBy['direction']): ISortBy['direction'] => ( + direction === 'asc' || direction === 'desc' ? direction : defaultDirection +); + +export interface DataViewSortConfig { + /** Attribute to sort the entries by */ + sortBy: string | undefined; + /** Sort direction */ + direction: ISortBy['direction']; +}; + +export interface UseDataViewSortProps { + /** Initial sort config */ + initialSort?: DataViewSortConfig; + /** Current search parameters as a string */ + searchParams?: URLSearchParams; + /** Function to set search parameters */ + setSearchParams?: (params: URLSearchParams) => void; + /** Default direction */ + defaultDirection?: ISortBy['direction']; + /** Sort by URL param name */ + sortByParam?: string; + /** Direction URL param name */ + directionParam?: string; +}; + +export const useDataViewSort = (props?: UseDataViewSortProps) => { + const { + initialSort, + searchParams, + setSearchParams, + defaultDirection = 'asc', + sortByParam = DataViewSortParams.SORT_BY, + directionParam = DataViewSortParams.DIRECTION + } = props ?? {}; + + const isUrlSyncEnabled = useMemo(() => searchParams && !!setSearchParams, [ searchParams, setSearchParams ]); + + const [ state, setState ] = useState({ + sortBy: searchParams?.get(sortByParam) ?? initialSort?.sortBy, + direction: validateDirection(searchParams?.get(directionParam) as ISortBy['direction'], initialSort?.direction), + }); + + const updateSearchParams = (sortBy: string, direction: ISortBy['direction']) => { + if (isUrlSyncEnabled && sortBy) { + const params = new URLSearchParams(searchParams); + params.set(sortByParam, `${sortBy}`); + params.set(directionParam, `${direction}`); + setSearchParams?.(params); + } + }; + + useEffect(() => { + state.sortBy && state.direction && updateSearchParams(state.sortBy, state.direction); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const currentSortBy = searchParams?.get(sortByParam) || state.sortBy; + const currentDirection = searchParams?.get(directionParam) as ISortBy['direction'] || state.direction; + const validDirection = validateDirection(currentDirection, defaultDirection); + currentSortBy !== state.sortBy || validDirection !== state.direction && setState({ sortBy: currentSortBy, direction: validDirection }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ searchParams?.toString() ]); + + const onSort = ( + _event: React.MouseEvent | React.KeyboardEvent | MouseEvent | undefined, + newSortBy: string, + newSortDirection: ISortBy['direction'] + ) => { + setState({ sortBy: newSortBy, direction: newSortDirection }); + updateSearchParams(newSortBy, newSortDirection); + }; + + return { + ...state, + onSort + }; +}; diff --git a/packages/module/src/InternalContext/InternalContext.tsx b/packages/module/src/InternalContext/InternalContext.tsx index bef1b22..740c1a8 100644 --- a/packages/module/src/InternalContext/InternalContext.tsx +++ b/packages/module/src/InternalContext/InternalContext.tsx @@ -17,6 +17,7 @@ export interface InternalContextProps { activeState?: DataViewState | string; } +/** extends InternalContextProps */ export interface InternalContextValue extends InternalContextProps { /** Flag indicating if data view is selectable (auto-calculated) */ isSelectable: boolean; diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts index 1ce1801..aca7f76 100644 --- a/packages/module/src/index.ts +++ b/packages/module/src/index.ts @@ -7,6 +7,9 @@ export * from './Hooks'; export { default as DataViewToolbar } from './DataViewToolbar'; export * from './DataViewToolbar'; +export { default as DataViewTextFilter } from './DataViewTextFilter'; +export * from './DataViewTextFilter'; + export { default as DataViewTableTree } from './DataViewTableTree'; export * from './DataViewTableTree'; @@ -22,5 +25,8 @@ export * from './DataViewTable'; export { default as DataViewEventsContext } from './DataViewEventsContext'; export * from './DataViewEventsContext'; +export { default as DataViewCheckboxFilter } from './DataViewCheckboxFilter'; +export * from './DataViewCheckboxFilter'; + export { default as DataView } from './DataView'; export * from './DataView';