import { parseColor } from '@ni/fast-colors';
import { tableHeaderTag } from '../components/header';
import { TableColumnSortDirection, TableRowSelectionState } from '../types';
import { waitForUpdatesAsync } from '../../testing/async-helpers';
import { menuButtonTag } from '../../menu-button';
import { tableCellTag } from '../components/cell';
import { TableCellView } from '../../table-column/base/cell-view';
import { tableRowTag } from '../components/row';
import { Anchor, anchorTag } from '../../anchor';
import { tableGroupRowTag } from '../components/group-row';
import { Icon } from '../../icon-base';
import { Spinner, spinnerTag } from '../../spinner';
import { borderHoverColor } from '../../theme-provider/design-tokens';
/**
 * Page object for the `nimble-table` component to provide consistent ways
 * of querying and interacting with the component during tests.
 */
export class TablePageObject {
    constructor(tableElement) {
        this.tableElement = tableElement;
        this.isSlotElement = (element) => {
            return element?.nodeName === 'SLOT';
        };
    }
    getRenderedHeaderCount() {
        const headers = this.tableElement.shadowRoot.querySelectorAll(tableHeaderTag);
        return headers.length;
    }
    getRenderedCellCountForRow(rowIndex) {
        const row = this.getRow(rowIndex);
        const cells = row.shadowRoot.querySelectorAll(tableCellTag);
        return cells.length;
    }
    getHeaderContent(columnIndex) {
        const headers = this.tableElement.shadowRoot.querySelectorAll(tableHeaderTag);
        if (columnIndex >= headers.length) {
            throw new Error('Attempting to index past the total number of rendered columns');
        }
        return this.getHeaderContentElement(headers.item(columnIndex));
    }
    getHeaderElement(columnIndex) {
        const headers = this.tableElement.shadowRoot.querySelectorAll(tableHeaderTag);
        if (columnIndex >= headers.length) {
            throw new Error('Attempting to index past the total number of rendered columns');
        }
        return headers.item(columnIndex);
    }
    getHeaderTitle(columnIndex) {
        const column = this.tableElement.columns[columnIndex];
        return (column?.shadowRoot.firstElementChild?.getAttribute('title') ?? '');
    }
    getHeaderTextContent(columnIndex) {
        return (this.getHeaderContent(columnIndex)?.firstChild?.textContent?.trim() ?? '');
    }
    dispatchEventToHeader(columnIndex, event) {
        const column = this.tableElement.columns[columnIndex];
        return column?.shadowRoot.firstElementChild?.dispatchEvent(event);
    }
    getHeaderRenderedWidth(columnIndex) {
        const headers = this.tableElement.shadowRoot.querySelectorAll(tableHeaderTag);
        if (columnIndex >= headers.length) {
            throw new Error('Attempting to index past the total number of rendered columns');
        }
        return headers[columnIndex].getBoundingClientRect().width;
    }
    async clickColumnHeader(columnIndex, shiftKeyDown = false) {
        const clickEvent = new MouseEvent('click', {
            shiftKey: shiftKeyDown,
            bubbles: true
        });
        this.getHeaderElement(columnIndex).dispatchEvent(clickEvent);
        await waitForUpdatesAsync();
    }
    getRenderedRowCount() {
        return this.tableElement.shadowRoot.querySelectorAll(tableRowTag)
            .length;
    }
    getRenderedGroupRowCount() {
        return this.tableElement.shadowRoot.querySelectorAll(tableGroupRowTag)
            .length;
    }
    getAllGroupRowsExpandedState() {
        const groupRows = this.tableElement.shadowRoot.querySelectorAll(tableGroupRowTag);
        return Array.from(groupRows).map(row => row.expanded);
    }
    getAllDataRowsExpandedState() {
        const rows = this.tableElement.shadowRoot.querySelectorAll(tableRowTag);
        return Array.from(rows).map(row => row.expanded);
    }
    getRenderedCellView(rowIndex, columnIndex) {
        const cell = this.getCell(rowIndex, columnIndex);
        const cellView = cell.cellViewContainer.firstElementChild;
        if (!(cellView instanceof TableCellView)) {
            throw new Error('Cell view not found in cell - ensure cellViewTag is set for column');
        }
        return cellView;
    }
    getRenderedCellViewById(recordId, columnId) {
        const cell = this.getCellById(recordId, columnId);
        const cellView = cell.cellViewContainer.firstElementChild;
        if (!(cellView instanceof TableCellView)) {
            throw new Error('Cell view not found in cell - ensure cellViewTag is set for column');
        }
        return cellView;
    }
    getRenderedCellTextContent(rowIndex, columnIndex) {
        return (this.getRenderedCellView(rowIndex, columnIndex).shadowRoot.textContent?.trim() ?? '');
    }
    getRenderedCellAnchor(rowIndex, columnIndex) {
        const anchor = this.getRenderedCellView(rowIndex, columnIndex).shadowRoot.querySelector(anchorTag);
        if (!anchor) {
            throw new Error(`Anchor not found at cell ${rowIndex},${columnIndex}`);
        }
        return anchor;
    }
    getRenderedMappingColumnCellIconTagName(rowIndex, columnIndex) {
        const iconOrSpinner = this.getRenderedMappingColumnIconOrSpinner(this.getRenderedCellView(rowIndex, columnIndex));
        return iconOrSpinner.tagName.toLocaleLowerCase();
    }
    getRenderedMappingColumnCellIconSeverity(rowIndex, columnIndex) {
        const iconOrSpinner = this.getRenderedMappingColumnIconOrSpinner(this.getRenderedCellView(rowIndex, columnIndex));
        if (iconOrSpinner instanceof Icon) {
            return iconOrSpinner.severity;
        }
        throw new Error('Cell does not contain an icon');
    }
    getRenderedGroupHeaderTextContent(groupRowIndex) {
        return (this.getGroupRowHeaderView(groupRowIndex).shadowRoot.textContent?.trim() ?? '');
    }
    getAllRenderedGroupHeaderTextContent() {
        const groupRows = this.tableElement.shadowRoot.querySelectorAll(tableGroupRowTag);
        return Array.from(groupRows).map((_, i) => {
            return this.getRenderedGroupHeaderTextContent(i);
        });
    }
    getCellTitle(rowIndex, columnIndex) {
        const cellView = this.getRenderedCellView(rowIndex, columnIndex);
        return (cellView.shadowRoot.firstElementChild?.getAttribute('title') ?? '');
    }
    dispatchEventToCell(rowIndex, columnIndex, event) {
        const cellView = this.getRenderedCellView(rowIndex, columnIndex);
        return cellView.shadowRoot.firstElementChild?.dispatchEvent(event);
    }
    getGroupHeaderTitle(groupRowIndex) {
        const groupHeader = this.getGroupRowHeaderView(groupRowIndex);
        return (groupHeader
            .shadowRoot.querySelector('span')
            ?.getAttribute('title') ?? '');
    }
    dispatchEventToGroupHeader(groupRowIndex, event) {
        const groupHeader = this.getGroupRowHeaderView(groupRowIndex);
        return groupHeader
            .shadowRoot.querySelector('span')
            ?.dispatchEvent(event);
    }
    getRecordId(rowIndex) {
        return this.getRow(rowIndex).recordId;
    }
    getRowWidth() {
        const tableRowContainer = this.tableElement.shadowRoot.querySelector('.table-row-container');
        return tableRowContainer.scrollWidth;
    }
    async sizeTableToGivenRowWidth(rowWidth, table) {
        if (!table.$fastController.isConnected) {
            throw Error('The element must be connected before calling this method');
        }
        table.style.width = `${rowWidth
            + table.headerRowActionContainer.getBoundingClientRect().width
            + table.virtualizer.headerContainerMarginRight}px`;
        await waitForUpdatesAsync();
    }
    getCellRenderedWidth(rowIndex, columnIndex) {
        const cell = this.getCell(rowIndex, columnIndex);
        const actualWidth = cell.getBoundingClientRect().width;
        // Round to one decimal place. This is to work around a bug in Chrome related to
        // fractional widths (e.g. '1fr') in grid layouts that results in some numerical
        // precision issues. See: https://bugs.chromium.org/p/chromium/issues/detail?id=1515685
        return Math.round(actualWidth * 10) / 10;
    }
    getTotalCellRenderedWidth() {
        const row = this.getRow(0);
        const cells = row?.shadowRoot?.querySelectorAll(tableCellTag);
        return Array.from(cells).reduce((p, c) => {
            return p + c.getBoundingClientRect().width;
        }, 0);
    }
    async scrollToFirstRowAsync() {
        const scrollElement = this.tableElement.viewport;
        scrollElement.scroll({ top: 0 });
        await waitForUpdatesAsync();
    }
    async scrollToLastRowAsync() {
        const scrollElement = this.tableElement.viewport;
        scrollElement.scroll({ top: scrollElement.scrollHeight });
        await waitForUpdatesAsync();
    }
    getCellActionMenu(rowIndex, columnIndex) {
        return this.getCell(rowIndex, columnIndex).shadowRoot.querySelector(menuButtonTag);
    }
    async clickCellActionMenu(rowIndex, columnIndex) {
        this.setRowHoverState(rowIndex, true);
        await waitForUpdatesAsync();
        const menuButton = this.getCellActionMenu(rowIndex, columnIndex);
        if (!menuButton) {
            throw new Error('Cannot click on a non-visible action menu');
        }
        menuButton.toggleButton.control.click();
    }
    isCellActionMenuVisible(rowIndex, columnIndex) {
        const actionMenu = this.getCellActionMenu(rowIndex, columnIndex);
        if (!actionMenu) {
            return false;
        }
        return window.getComputedStyle(actionMenu).display !== 'none';
    }
    setRowHoverState(rowIndex, hover) {
        const row = this.getRow(rowIndex);
        const cells = row.shadowRoot.querySelectorAll(tableCellTag);
        if (hover) {
            cells.forEach(cell => cell.style.setProperty('--ni-private-table-cell-action-menu-display', 'block'));
        }
        else {
            cells.forEach(cell => cell.style.removeProperty('--ni-private-table-cell-action-menu-display'));
        }
    }
    async clickGroupRow(groupRowIndex) {
        const groupRow = this.getGroupRow(groupRowIndex);
        groupRow.click();
        await waitForUpdatesAsync();
    }
    async clickRow(rowIndex, modifiers = {}) {
        const row = this.getRow(rowIndex);
        const event = new MouseEvent('click', modifiers);
        row.dispatchEvent(event);
        await waitForUpdatesAsync();
    }
    getIsRowSelectable(rowIndex) {
        const row = this.getRow(rowIndex);
        return row.selectable;
    }
    getIsRowSelected(rowIndex) {
        const row = this.getRow(rowIndex);
        return row.selected;
    }
    toggleGroupRowExpandedState(groupRowIndex) {
        this.getGroupRow(groupRowIndex).click();
    }
    clickCollapseAllButton() {
        this.getCollapseAllButton()?.click();
    }
    clickDataRowExpandCollapseButton(rowIndex) {
        const expandCollapseButton = this.getExpandCollapseButtonForRow(rowIndex);
        if (!expandCollapseButton) {
            throw new Error('The provided row index has no visible expand collapse button associated with it.');
        }
        expandCollapseButton.click();
    }
    isCollapseAllButtonVisible() {
        const collapseButton = this.getCollapseAllButton();
        if (collapseButton) {
            return (window.getComputedStyle(collapseButton).visibility === 'visible');
        }
        return false;
    }
    isCollapseAllButtonSpaceReserved() {
        const collapseButton = this.getCollapseAllButton();
        if (collapseButton) {
            return window.getComputedStyle(collapseButton).display !== 'none';
        }
        return true;
    }
    isDataRowExpandCollapseButtonVisible(rowIndex) {
        const expandCollapseButton = this.getExpandCollapseButtonForRow(rowIndex);
        return expandCollapseButton !== null;
    }
    isDataRowLoadingSpinnerVisible(rowIndex) {
        const row = this.getRow(rowIndex);
        const spinner = row.shadowRoot.querySelector(spinnerTag);
        return spinner !== null;
    }
    isTableSelectionCheckboxVisible() {
        const checkbox = this.getSelectionCheckboxForTable();
        return this.isCheckboxVisible(checkbox);
    }
    getTableSelectionState() {
        const checkbox = this.getSelectionCheckboxForTable();
        return this.getSelectionStateOfCheckbox(checkbox);
    }
    clickTableSelectionCheckbox() {
        const checkbox = this.getSelectionCheckboxForTable();
        checkbox.click();
    }
    isRowSelectionCheckboxVisible(rowIndex) {
        const checkbox = this.getSelectionCheckboxForRow(rowIndex);
        return this.isCheckboxVisible(checkbox);
    }
    getRowSelectionState(rowIndex) {
        const checkbox = this.getSelectionCheckboxForRow(rowIndex);
        return this.getSelectionStateOfCheckbox(checkbox);
    }
    clickRowSelectionCheckbox(rowIndex, shiftKey = false) {
        if (shiftKey) {
            const shiftKeyDownEvent = new KeyboardEvent('keydown', {
                key: 'Shift',
                shiftKey: true,
                bubbles: true
            });
            document.dispatchEvent(shiftKeyDownEvent);
        }
        const checkbox = this.getSelectionCheckboxForRow(rowIndex);
        checkbox.click();
        if (shiftKey) {
            const shiftKeyUpEvent = new KeyboardEvent('keyup', {
                key: 'Shift',
                bubbles: true
            });
            document.dispatchEvent(shiftKeyUpEvent);
        }
    }
    getGroupRowSelectionState(groupRowIndex) {
        const checkbox = this.getSelectionCheckboxForGroupRow(groupRowIndex);
        return this.getSelectionStateOfCheckbox(checkbox);
    }
    clickGroupRowSelectionCheckbox(groupRowIndex, shiftKey = false) {
        if (shiftKey) {
            const shiftKeyDownEvent = new KeyboardEvent('keydown', {
                key: 'Shift',
                shiftKey: true,
                bubbles: true
            });
            document.dispatchEvent(shiftKeyDownEvent);
        }
        const checkbox = this.getSelectionCheckboxForGroupRow(groupRowIndex);
        checkbox.click();
        if (shiftKey) {
            const shiftKeyUpEvent = new KeyboardEvent('keyup', {
                key: 'Shift',
                bubbles: true
            });
            document.dispatchEvent(shiftKeyUpEvent);
        }
    }
    /**
     * @param columnIndex The index of the column to the left of the divider to press.
     */
    pressRightColumnDivider(columnIndex) {
        this.pressColumnDivider(this.getColumnDivider(columnIndex, false));
    }
    /**
     * @param columnIndex The index of the column to the left of the divider to release.
     */
    releaseRightColumnDivider(columnIndex) {
        this.releaseColumnDivider(this.getColumnDivider(columnIndex, false));
    }
    /**
     * @param columnIndex The index of the column to the left of a divider being dragged. Thus, this
     * cannot be given a value representing the last visible column index.
     * @param deltas The series of mouse movements in the x-direction while sizing a column.
     */
    dragSizeColumnByRightDivider(columnIndex, deltas) {
        this.dragSizeColumnByDivider(columnIndex, false, deltas);
    }
    /**
     * @param columnIndex The index of the column to the right of a divider being dragged. Thus, this
     * value must be greater than 0 and less than the total number of visible columns.
     * @param deltas The series of mouse movements in the x-direction while sizing a column.
     */
    dragSizeColumnByLeftDivider(columnIndex, deltas) {
        this.dragSizeColumnByDivider(columnIndex, true, deltas);
    }
    /**
     * @param columnIndex The index of the column whose right divider we're checking.
     */
    columnRightDividerHasActiveStyling(columnIndex) {
        const divider = this.getColumnDivider(columnIndex, false);
        const currentColor = parseColor(window.getComputedStyle(divider).borderLeftColor);
        const hoverColor = parseColor(borderHoverColor.getValueFor(divider));
        return currentColor.equalValue(hoverColor);
    }
    isVerticalScrollbarVisible() {
        return (this.tableElement.viewport.clientHeight
            < this.tableElement.viewport.scrollHeight);
    }
    isHorizontalScrollbarVisible() {
        return (this.tableElement.viewport.clientWidth
            < this.tableElement.viewport.scrollWidth);
    }
    getSortedColumns() {
        return this.tableElement.columns
            .filter(x => !x.columnInternals.sortingDisabled
            && typeof x.columnInternals.currentSortIndex === 'number'
            && x.columnInternals.currentSortDirection
                !== TableColumnSortDirection.none)
            .sort((a, b) => a.columnInternals.currentSortIndex
            - b.columnInternals.currentSortIndex)
            .map(x => {
            return {
                columnId: x.columnId,
                sortDirection: x.columnInternals.currentSortDirection
            };
        });
    }
    getGroupedColumns() {
        return this.tableElement.columns
            .filter(x => !x.columnInternals.groupingDisabled
            && typeof x.columnInternals.groupIndex === 'number')
            .sort((a, b) => a.columnInternals.groupIndex
            - b.columnInternals.groupIndex)
            .map(x => x.columnId ?? '');
    }
    getChildRowCountForGroup(groupRowIndex) {
        const groupRow = this.getGroupRow(groupRowIndex);
        const countDisplayString = groupRow
            .shadowRoot.querySelector('.group-row-child-count')
            .textContent.trim();
        // Remove the parenthesis to get just the number as a string
        const countString = countDisplayString.substring(1, countDisplayString.length - 1);
        return Number(countString);
    }
    /** @internal */
    isRowHoverStylingEnabled() {
        const rows = this.tableElement.shadowRoot.querySelectorAll(`${tableRowTag}, ${tableGroupRowTag}`);
        const firstRowAllowsHover = rows[0].allowHover;
        if (Array.from(rows).some(x => x.allowHover !== firstRowAllowsHover)) {
            throw new Error('Rows inconsistently allow hover styling');
        }
        return firstRowAllowsHover;
    }
    /** @internal */
    getGroupRowHeaderView(groupRowIndex) {
        const groupRow = this.getGroupRow(groupRowIndex);
        return groupRow.shadowRoot.querySelector('.group-header-view');
    }
    /** @internal */
    getRow(rowIndex) {
        const rows = this.tableElement.shadowRoot.querySelectorAll(tableRowTag);
        if (rowIndex >= rows.length) {
            throw new Error('Attempting to index past the total number of rendered rows');
        }
        return rows.item(rowIndex);
    }
    /** @internal */
    getCell(rowIndex, columnIndex) {
        const row = this.getRow(rowIndex);
        const cells = row.shadowRoot.querySelectorAll(tableCellTag);
        if (columnIndex >= cells.length) {
            throw new Error('Attempting to index past the total number of rendered columns');
        }
        return cells.item(columnIndex);
    }
    getRowById(recordId) {
        const row = this.tableElement.shadowRoot.querySelector(`nimble-table-row[record-id="${CSS.escape(recordId)}"]`);
        if (!row) {
            throw new Error('Row with given id was not found. It may not be scrolled into view.');
        }
        return row;
    }
    getCellById(recordId, columnId) {
        const row = this.getRowById(recordId);
        const cell = row.shadowRoot.querySelector(`nimble-table-cell[column-id="${CSS.escape(columnId)}"]`);
        if (!cell) {
            throw new Error('Cell with given columnId was not found in row');
        }
        return cell;
    }
    getCollapseAllButton() {
        return this.tableElement.shadowRoot.querySelector('.collapse-all-button');
    }
    getExpandCollapseButtonForRow(rowIndex) {
        const row = this.getRow(rowIndex);
        const rowExpandCollapseButton = row.shadowRoot.querySelector('.expand-collapse-button');
        if (!rowExpandCollapseButton) {
            const firstCell = this.getCell(rowIndex, 0);
            return firstCell?.shadowRoot.querySelector('.expand-collapse-button');
        }
        return rowExpandCollapseButton;
    }
    getSelectionCheckboxForRow(rowIndex) {
        const row = this.getRow(rowIndex);
        return row.shadowRoot.querySelector('.selection-checkbox');
    }
    getSelectionCheckboxForGroupRow(groupRowIndex) {
        const groupRow = this.getGroupRow(groupRowIndex);
        return groupRow.shadowRoot.querySelector('.selection-checkbox');
    }
    getSelectionCheckboxForTable() {
        return this.tableElement.shadowRoot.querySelector('.header-row .selection-checkbox');
    }
    isCheckboxVisible(checkbox) {
        return !!checkbox && !checkbox.hidden;
    }
    getSelectionStateOfCheckbox(checkbox) {
        if (!checkbox) {
            throw new Error('Cannot get selection state from null checkbox');
        }
        if (checkbox.indeterminate) {
            return TableRowSelectionState.partiallySelected;
        }
        if (checkbox.checked) {
            return TableRowSelectionState.selected;
        }
        return TableRowSelectionState.notSelected;
    }
    getGroupRow(groupRowIndex) {
        const groupRows = this.tableElement.shadowRoot.querySelectorAll(tableGroupRowTag);
        if (groupRowIndex >= groupRows.length) {
            throw new Error('Attempting to index past the total number of group rows');
        }
        return groupRows.item(groupRowIndex);
    }
    getHeaderContentElement(element) {
        const nodeChildren = this.isSlotElement(element)
            ? element.assignedNodes()
            : element.shadowRoot?.childNodes;
        if (!nodeChildren) {
            return undefined;
        }
        const slotElement = Array.from(nodeChildren)?.find(this.isSlotElement);
        if (slotElement) {
            return this.getHeaderContentElement(slotElement);
        }
        return nodeChildren[0]; // header content should be first item in final slot element
    }
    getRenderedMappingColumnIconOrSpinner(view) {
        const viewShadowRoot = view.shadowRoot;
        const spinnerOrIcon = viewShadowRoot.querySelector('.reserve-icon-size')?.firstElementChild;
        if (!(spinnerOrIcon instanceof Icon || spinnerOrIcon instanceof Spinner)) {
            throw new Error('Icon or Spinner not found');
        }
        return spinnerOrIcon;
    }
    getColumnDivider(columnIndex, leftDivider) {
        const headerContainers = this.tableElement.shadowRoot.querySelectorAll('.header-container');
        if (columnIndex < 0 || columnIndex >= headerContainers.length) {
            throw new Error('Invalid column index. Index must be greater than or equal to 0 and less than the number of visible columns.');
        }
        const side = leftDivider ? 'left' : 'right';
        const divider = headerContainers[columnIndex].querySelector(`.column-divider.${side}`);
        if (!divider) {
            throw new Error(`The provided column index has no ${side} divider associated with it.`);
        }
        return divider;
    }
    pressColumnDivider(divider) {
        const dividerRect = divider.getBoundingClientRect();
        const clickX = (dividerRect.x + dividerRect.width) / 2;
        const pointerDownEvent = new PointerEvent('pointerdown', {
            // -1 is a value reserved for synthetic events: https://w3c.github.io/pointerevents/#dom-pointerevent-pointerid
            pointerId: -1,
            clientX: clickX,
            clientY: (dividerRect.y + dividerRect.height) / 2
        });
        divider.dispatchEvent(pointerDownEvent);
        return clickX;
    }
    releaseColumnDivider(divider) {
        divider.dispatchEvent(new PointerEvent('pointerup'));
    }
    dragSizeColumnByDivider(columnIndex, leftDivider, deltas) {
        const divider = this.getColumnDivider(columnIndex, leftDivider);
        let currentPointerX = this.pressColumnDivider(divider);
        for (const delta of deltas) {
            currentPointerX += delta;
            const pointerMoveEvent = new PointerEvent('pointermove', {
                clientX: currentPointerX
            });
            divider.dispatchEvent(pointerMoveEvent);
        }
        this.releaseColumnDivider(divider);
    }
}
//# sourceMappingURL=table.pageobject.js.map