Skip to content
This repository has been archived by the owner on Mar 19, 2024. It is now read-only.

Commit

Permalink
feat: update left side as you resize column (#443)
Browse files Browse the repository at this point in the history
  • Loading branch information
haxxmaxx authored Nov 3, 2023
1 parent 68a82c0 commit f877c5f
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 64 deletions.
2 changes: 2 additions & 0 deletions src/pivot-table/components/PivotTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const StickyPivotTable = ({
showLastRightBorder,
getRightGridColumnWidth,
getHeaderCellsIconsVisibilityStatus,
overrideLeftGridWidth,
} = useColumnWidth(
layoutService,
tableRect,
Expand Down Expand Up @@ -162,6 +163,7 @@ export const StickyPivotTable = ({
translator={translator}
changeSortOrder={changeSortOrder}
changeActivelySortedHeader={changeActivelySortedHeader}
overrideLeftGridWidth={overrideLeftGridWidth}
/>

<LeftGrid
Expand Down
6 changes: 4 additions & 2 deletions src/pivot-table/components/cells/ColumnAdjuster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { AdjusterCellInfo, DataModel } from "../../../types/types";
import { GRID_BORDER } from "../../constants";
import { useBaseContext } from "../../contexts/BaseProvider";
import { useSelectionsContext } from "../../contexts/SelectionsProvider";
import { ColumnWidthValues } from "../../hooks/use-column-width";
import { ColumnWidthValues, type OverrideLeftGridWidth } from "../../hooks/use-column-width";
import { CELL_PADDING } from "../shared-styles";
import { AdjusterBorder, AdjusterHitArea, COLUMN_ADJUSTER_BORDER_CLASS, COLUMN_ADJUSTER_CLASS } from "./styles";

Expand All @@ -14,13 +14,14 @@ interface AdjusterProps {
columnWidth: number;
dataModel: DataModel | undefined;
isLastColumn: boolean;
overrideLeftGridWidth?: OverrideLeftGridWidth;
}
/**
* Component that is placed on top of column border, to resize the columns.
* When you start dragging, mouse move and mouse up listeners are added.
* While dragging this components follows the pointer, and on mouse up all column widths are updated.
*/
const ColumnAdjuster = ({ cellInfo, columnWidth, dataModel, isLastColumn }: AdjusterProps) => {
const ColumnAdjuster = ({ cellInfo, columnWidth, dataModel, isLastColumn, overrideLeftGridWidth }: AdjusterProps) => {
const { interactions } = useBaseContext();
const { isActive } = useSelectionsContext();
const [, forceRerender] = useState({});
Expand All @@ -36,6 +37,7 @@ const ColumnAdjuster = ({ cellInfo, columnWidth, dataModel, isLastColumn }: Adju
const adjustedWidth = Math.max(tempWidth.initWidth + deltaWidth, ColumnWidthValues.PixelsMin);
forceRerender({});
tempWidth.columnWidth = adjustedWidth;
overrideLeftGridWidth?.(adjustedWidth, cellInfo.dimensionInfoIndex);
};

const mouseUpHandler = (evt: MouseEvent) => {
Expand Down
5 changes: 4 additions & 1 deletion src/pivot-table/components/cells/DimensionTitleCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { ChangeActivelySortedHeader, ChangeSortOrder, DataModel, HeaderCell
import { HEADER_ICON_SIZE } from "../../constants";
import { useBaseContext } from "../../contexts/BaseProvider";
import { useStyleContext } from "../../contexts/StyleProvider";
import type { GetHeaderCellsIconsVisibilityStatus } from "../../hooks/use-column-width";
import type { GetHeaderCellsIconsVisibilityStatus, OverrideLeftGridWidth } from "../../hooks/use-column-width";
import { useHeadCellDim } from "../../hooks/use-head-cell-dim";
import { baseCellStyle, getBorderStyle } from "../shared-styles";
import ColumnAdjuster from "./ColumnAdjuster";
Expand All @@ -24,6 +24,7 @@ interface DimensionTitleCellProps {
changeActivelySortedHeader: ChangeActivelySortedHeader;
iconsVisibilityStatus: ReturnType<GetHeaderCellsIconsVisibilityStatus>;
columnWidth: number;
overrideLeftGridWidth: OverrideLeftGridWidth;
}

export const testId = "title-cell";
Expand All @@ -44,6 +45,7 @@ const DimensionTitleCell = ({
changeActivelySortedHeader,
iconsVisibilityStatus,
columnWidth,
overrideLeftGridWidth,
}: DimensionTitleCellProps): JSX.Element => {
const listboxRef = useRef<HTMLDivElement>(null);
const styleService = useStyleContext();
Expand Down Expand Up @@ -122,6 +124,7 @@ const DimensionTitleCell = ({
columnWidth={columnWidth}
dataModel={dataModel}
isLastColumn={isLastColumn}
overrideLeftGridWidth={overrideLeftGridWidth}
/>
</StyledHeaderCellWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe("DimensionTitleCell", () => {
const translator = { get: (s) => s } as stardust.Translator;
const changeSortOrder = jest.fn();
const changeActivelySortedColumn = jest.fn();
const overrideLeftGridWidth = jest.fn();
const style: React.CSSProperties = {
position: "relative",
left: "25px",
Expand All @@ -28,7 +29,6 @@ describe("DimensionTitleCell", () => {
shouldShowMenuIcon: true,
shouldShowLockIcon: true,
};

let component: React.JSX.Element;

beforeEach(() => {
Expand All @@ -43,6 +43,7 @@ describe("DimensionTitleCell", () => {
changeActivelySortedHeader={changeActivelySortedColumn}
iconsVisibilityStatus={iconsVisibilityStatus}
columnWidth={100}
overrideLeftGridWidth={overrideLeftGridWidth}
/>
);
});
Expand Down
5 changes: 4 additions & 1 deletion src/pivot-table/components/grids/HeaderGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { stardust } from "@nebula.js/stardust";
import React, { memo } from "react";
import type { ChangeActivelySortedHeader, ChangeSortOrder, DataModel, HeadersData } from "../../../types/types";
import type { GetHeaderCellsIconsVisibilityStatus } from "../../hooks/use-column-width";
import type { GetHeaderCellsIconsVisibilityStatus, OverrideLeftGridWidth } from "../../hooks/use-column-width";
import DimensionTitleCell from "../cells/DimensionTitleCell";
import EmptyHeaderCell from "../cells/EmptyHeaderCell";

Expand All @@ -15,6 +15,7 @@ interface HeaderGridProps {
changeActivelySortedHeader: ChangeActivelySortedHeader;
getHeaderCellsIconsVisibilityStatus: GetHeaderCellsIconsVisibilityStatus;
height: number;
overrideLeftGridWidth: OverrideLeftGridWidth;
}

const containerStyle: React.CSSProperties = {
Expand All @@ -32,6 +33,7 @@ const HeaderGrid = ({
changeActivelySortedHeader,
getHeaderCellsIconsVisibilityStatus,
height,
overrideLeftGridWidth,
}: HeaderGridProps): JSX.Element | null => (
<div
style={{
Expand Down Expand Up @@ -64,6 +66,7 @@ const HeaderGrid = ({
cell={cell}
iconsVisibilityStatus={iconsVisibilityStatus}
columnWidth={columnWidths[colIndex]}
overrideLeftGridWidth={overrideLeftGridWidth}
/>,
);
}
Expand Down
51 changes: 41 additions & 10 deletions src/pivot-table/hooks/__tests__/use-column-width.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type MeasureTextHook,
type UseMeasureTextProps,
} from "@qlik/nebula-table-utils/lib/hooks";
import { renderHook } from "@testing-library/react";
import { act, renderHook } from "@testing-library/react";
import type { ExtendedDimensionInfo, ExtendedMeasureInfo } from "../../../types/QIX";
import { ColumnWidthType } from "../../../types/QIX";
import type { HeadersData, LayoutService, Rect, VisibleDimensionInfo } from "../../../types/types";
Expand Down Expand Up @@ -75,6 +75,7 @@ describe("useColumnWidth", () => {
mockedUseMeasureText.mockReturnValue(mockedMeasureText);
verticalScrollbarWidth = 0;
horizontalScrollbarHeightSetter = jest.fn();
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
});

afterEach(() => {
Expand All @@ -84,17 +85,16 @@ describe("useColumnWidth", () => {
const renderUseColumnWidth = () => {
const {
result: { current },
} = renderHook(() => {
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
return useColumnWidth(
} = renderHook(() =>
useColumnWidth(
layoutService,
rect,
headersData,
visibleTopDimensionInfo,
verticalScrollbarWidth,
horizontalScrollbarHeightSetter,
);
});
),
);
return current;
};

Expand Down Expand Up @@ -132,6 +132,7 @@ describe("useColumnWidth", () => {
qGroupFieldDefs: [""],
} as ExtendedDimensionInfo;
visibleLeftDimensionInfo = [dimInfo, dimInfoWithoutPixels, dimInfoWithNaN];
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);

const { leftGridColumnWidths } = renderUseColumnWidth();
expect(leftGridColumnWidths[0]).toBe(pixels);
Expand All @@ -154,6 +155,7 @@ describe("useColumnWidth", () => {
qGroupFieldDefs: [""],
} as ExtendedDimensionInfo;
visibleLeftDimensionInfo = [dimInfo, dimInfoWithoutPixels, dimInfoWithNaN];
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);

const { leftGridColumnWidths } = renderUseColumnWidth();
expect(leftGridColumnWidths[0]).toBe(percentage * percentageConversion);
Expand All @@ -172,6 +174,7 @@ describe("useColumnWidth", () => {
{ columnWidth: { type: ColumnWidthType.Percentage, percentage: 10 } } as ExtendedMeasureInfo,
{ columnWidth: { type: ColumnWidthType.Pixels, pixels: 60 } } as ExtendedMeasureInfo,
];
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);

const { leftGridColumnWidths } = renderUseColumnWidth();
expect(leftGridColumnWidths[3]).toBe(60);
Expand All @@ -188,12 +191,37 @@ describe("useColumnWidth", () => {
qGroupFieldDefs: [""],
} as ExtendedDimensionInfo;
visibleLeftDimensionInfo = [dimInfo, dimInfo, dimInfoWithoutPixels];
const { leftGridColumnWidths } = renderUseColumnWidth();
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);

const { leftGridColumnWidths } = renderUseColumnWidth();
expect(leftGridColumnWidths[0]).toBe(pixels);
expect(leftGridColumnWidths[1]).toBe(pixels);
expect(leftGridColumnWidths[2]).toBe(ColumnWidthValues.PixelsDefault);
});

test("should return left column width when overridden using overrideLeftColumnWidth", () => {
const width = 25;
mockEstimateWidth(width);
mockMeasureText(width);

// Need to render this explicitly, since renderUseColumnWidth returns current, and thus leftGridColumnWidths wont update after overrideLeftGridWidth()
const { result } = renderHook(() =>
useColumnWidth(
layoutService,
rect,
headersData,
visibleTopDimensionInfo,
verticalScrollbarWidth,
horizontalScrollbarHeightSetter,
),
);

act(() => result.current.overrideLeftGridWidth(width * 3, 0));

expect(result.current.leftGridColumnWidths[0]).toBe(width * 3);
expect(result.current.leftGridColumnWidths[1]).toBe(width + EXPAND_ICON_SIZE + TOTAL_CELL_PADDING);
expect(result.current.leftGridColumnWidths[2]).toBe(width + TOTAL_CELL_PADDING + MENU_ICON_SIZE);
});
});

describe("getRightGridColumnWidth", () => {
Expand All @@ -209,6 +237,7 @@ describe("useColumnWidth", () => {
} as ExtendedDimensionInfo,
];
visibleTopDimensionInfo = [dimInfo, dimInfo, -1];
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
});

test("should return right column width when columnWidth is undefined", () => {
Expand Down Expand Up @@ -415,15 +444,15 @@ describe("useColumnWidth", () => {
});
});

describe("getHeaderCellsIconsVisibilityStatus()", () => {
describe("getHeaderCellsIconsVisibilityStatus", () => {
const columnWidthInPixels = 100;

test("should return `shouldShowMenuIcon` as true, b/c estimated width for text is small and there is enough space in each column", () => {
dimInfo.columnWidth = {
type: ColumnWidthType.Pixels,
pixels: columnWidthInPixels,
};

headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
mockMeasureText(columnWidthInPixels - TOTAL_CELL_PADDING - MENU_ICON_SIZE);

const { getHeaderCellsIconsVisibilityStatus } = renderUseColumnWidth();
Expand All @@ -438,6 +467,7 @@ describe("useColumnWidth", () => {
type: ColumnWidthType.Pixels,
pixels: columnWidthInPixels + MENU_ICON_SIZE - 1, // -1 is what makes the test pass
};
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
mockMeasureText(columnWidthInPixels - TOTAL_CELL_PADDING);

const { getHeaderCellsIconsVisibilityStatus } = renderUseColumnWidth();
Expand All @@ -453,7 +483,7 @@ describe("useColumnWidth", () => {
type: ColumnWidthType.Pixels,
pixels: columnWidthInPixels,
};

headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
mockMeasureText(columnWidthInPixels - TOTAL_CELL_PADDING - LOCK_ICON_SIZE - MENU_ICON_SIZE);

const { getHeaderCellsIconsVisibilityStatus } = renderUseColumnWidth();
Expand All @@ -468,6 +498,7 @@ describe("useColumnWidth", () => {
type: ColumnWidthType.Pixels,
pixels: columnWidthInPixels,
};
headersData = createHeadersData(layoutService, visibleTopDimensionInfo, visibleLeftDimensionInfo);
// Mock the measureTextForHeader call inside getHeaderCellsIconsVisibilityStatus()
mockMeasureText(columnWidthInPixels - TOTAL_CELL_PADDING - LOCK_ICON_SIZE);

Expand Down
Loading

0 comments on commit f877c5f

Please sign in to comment.