Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const ActionAccordion = ({ affectedTasks, note, setNote }: Props) => {
columns={columns}
data={affectedTasks.task_instances}
displayMode="table"
modelName={translate("common:taskInstance_other")}
modelName="common:taskInstance"
noRowsMessage={translate("dags:runAndTaskActions.affectedTasks.noItemsFound")}
total={affectedTasks.total_entries}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const AssetEvents = ({
displayMode="card"
initialState={tableUrlState}
isLoading={isLoading}
modelName={translate("common:assetEvent_one")}
modelName="common:assetEvent"
noRowsMessage={translate("noAssetEvents")}
onStateChange={setTableUrlState}
skeletonCount={5}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe("DataTable", () => {
columns={columns}
data={data}
initialState={{ pagination, sorting: [] }}
modelName="task"
onStateChange={onStateChange}
total={2}
/>,
Expand All @@ -69,6 +70,7 @@ describe("DataTable", () => {
columns={columns}
data={[{ name: "John Doe" }]}
initialState={{ pagination, sorting: [] }}
modelName="task"
onStateChange={onStateChange}
total={2}
/>,
Expand All @@ -89,6 +91,7 @@ describe("DataTable", () => {
pagination: { pageIndex: 0, pageSize: 10 },
sorting: [],
}}
modelName="task"
onStateChange={onStateChange}
total={2}
/>,
Expand All @@ -106,6 +109,7 @@ describe("DataTable", () => {
columns={columns}
data={data}
initialState={{ pagination, sorting: [] }}
modelName="task"
onStateChange={onStateChange}
total={2}
/>,
Expand All @@ -119,25 +123,28 @@ describe("DataTable", () => {
});

it("when isLoading renders skeleton columns", () => {
render(<DataTable columns={columns} data={data} isLoading />, {
render(<DataTable columns={columns} data={data} isLoading modelName="task" />, {
wrapper: ChakraWrapper,
});

expect(screen.getAllByTestId("skeleton")).toHaveLength(10);
});

it("still displays table if mode is card but there is no cardDef", () => {
render(<DataTable columns={columns} data={data} displayMode="card" />, {
render(<DataTable columns={columns} data={data} displayMode="card" modelName="task" />, {
wrapper: ChakraWrapper,
});

expect(screen.getByText("Name")).toBeInTheDocument();
});

it("displays cards if mode is card and there is cardDef", () => {
render(<DataTable cardDef={cardDef} columns={columns} data={data} displayMode="card" />, {
wrapper: ChakraWrapper,
});
render(
<DataTable cardDef={cardDef} columns={columns} data={data} displayMode="card" modelName="task" />,
{
wrapper: ChakraWrapper,
},
);

expect(screen.getByText("My name is John Doe.")).toBeInTheDocument();
});
Expand All @@ -150,6 +157,7 @@ describe("DataTable", () => {
data={data}
displayMode="card"
isLoading
modelName="task"
skeletonCount={5}
/>,
{
Expand All @@ -159,4 +167,86 @@ describe("DataTable", () => {

expect(screen.getAllByTestId("skeleton")).toHaveLength(5);
});

it("renders row count heading by default when total > 0", () => {
render(
<DataTable
columns={columns}
data={data}
initialState={{ pagination, sorting: [] }}
modelName="task"
total={2}
/>,
{ wrapper: ChakraWrapper },
);

expect(screen.getByRole("heading")).toHaveTextContent("2 task");
});

it("does not render row count heading when showRowCountHeading is false", () => {
render(
<DataTable
columns={columns}
data={data}
initialState={{ pagination, sorting: [] }}
modelName="task"
showRowCountHeading={false}
total={2}
/>,
{ wrapper: ChakraWrapper },
);

expect(screen.queryByRole("heading")).toBeNull();
});

it("uses translated zero-count model name in empty state", () => {
render(
<DataTable
columns={columns}
data={[]}
initialState={{ pagination, sorting: [] }}
modelName="task"
total={0}
/>,
{ wrapper: ChakraWrapper },
);

expect(screen.getByText(/noitemsFound/iu)).toBeInTheDocument();
});

it("renders display toggle when showDisplayToggle and onDisplayToggleChange are provided", () => {
const handleToggle = vi.fn();

render(
<DataTable
columns={columns}
data={data}
displayMode="table"
initialState={{ pagination, sorting: [] }}
modelName="task"
onDisplayToggleChange={handleToggle}
showDisplayToggle
total={2}
/>,
{ wrapper: ChakraWrapper },
);

expect(screen.getByLabelText(/toggleTableView/iu)).toBeInTheDocument();
});

it("does not render display toggle without showDisplayToggle", () => {
render(
<DataTable
columns={columns}
data={data}
displayMode="table"
initialState={{ pagination, sorting: [] }}
modelName="task"
total={2}
/>,
{ wrapper: ChakraWrapper },
);

expect(screen.queryByLabelText(/toggleTableView/iu)).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { HStack, Text } from "@chakra-ui/react";
import { Heading, HStack, Text } from "@chakra-ui/react";
import {
getCoreRowModel,
getExpandedRowModel,
Expand All @@ -34,6 +34,7 @@ import { useTranslation } from "react-i18next";

import { CardList } from "src/components/DataTable/CardList";
import { TableList } from "src/components/DataTable/TableList";
import { ToggleTableDisplay } from "src/components/DataTable/ToggleTableDisplay";
import { createSkeletonMock } from "src/components/DataTable/skeleton";
import type { CardDef, MetaColumn, TableState } from "src/components/DataTable/types";
import { ProgressBar, Pagination, Toaster } from "src/components/ui";
Expand All @@ -49,10 +50,13 @@ type DataTableProps<TData> = {
readonly initialState?: TableState;
readonly isFetching?: boolean;
readonly isLoading?: boolean;
readonly modelName?: string;
readonly modelName: string;
readonly noRowsMessage?: ReactNode;
readonly onDisplayToggleChange?: (mode: "card" | "table") => void;
readonly onStateChange?: (state: TableState) => void;
readonly renderSubComponent?: (props: { row: Row<TData> }) => React.ReactElement;
readonly showDisplayToggle?: boolean;
readonly showRowCountHeading?: boolean;
readonly skeletonCount?: number;
readonly total?: number;
};
Expand All @@ -72,7 +76,10 @@ export const DataTable = <TData,>({
isLoading,
modelName,
noRowsMessage,
onDisplayToggleChange,
onStateChange,
showDisplayToggle,
showRowCountHeading = true,
skeletonCount = 10,
total = 0,
}: DataTableProps<TData>) => {
Expand Down Expand Up @@ -135,11 +142,30 @@ export const DataTable = <TData,>({
// Default to show columns filter only if there are actually many columns displayed
const showColumnsFilter = allowFiltering ?? columns.length > 5;

const translateModelName = useCallback(
(count: number) => translate(modelName, { count }),
[modelName, translate],
);
const showRowCount = Boolean(
showRowCountHeading && !Boolean(isLoading) && !Boolean(isFetching) && total > 0,
);
const noRowsModelName = translateModelName(0);

const rowCountHeading = showRowCount ? (
<Heading py={3} size="md">
{`${total} ${translateModelName(total)}`}
</Heading>
) : undefined;

return (
<>
<ProgressBar size="xs" visibility={Boolean(isFetching) && !Boolean(isLoading) ? "visible" : "hidden"} />
{showDisplayToggle && onDisplayToggleChange ? (
<ToggleTableDisplay display={display} setDisplay={onDisplayToggleChange} />
) : undefined}
<Toaster />
{errorMessage}
{rowCountHeading}
{hasRows && display === "table" ? (
<TableList allowFiltering={showColumnsFilter} table={table} />
) : undefined}
Expand All @@ -148,7 +174,7 @@ export const DataTable = <TData,>({
) : undefined}
{!hasRows && !Boolean(isLoading) && (
<Text as="div" pl={4} pt={1}>
{noRowsMessage ?? translate("noItemsFound", { modelName })}
{noRowsMessage ?? translate("noItemsFound", { modelName: noRowsModelName })}
</Text>
)}
{hasPagination ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ export const AssetsList = () => {
<Heading py={3} size="md">
{data?.total_entries} {translate("common:asset", { count: data?.total_entries })}
</Heading>

<ExpandCollapseButtons
collapseLabel={translate("common:collapseAllExtra")}
expandLabel={translate("common:expandAllExtra")}
Expand All @@ -179,8 +178,9 @@ export const AssetsList = () => {
errorMessage={<ErrorAlert error={error} />}
initialState={tableURLState}
isLoading={isLoading}
modelName={translate("common:asset_one")}
modelName="common:asset"
onStateChange={setTableURLState}
showRowCountHeading={false}
total={data?.total_entries}
/>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const Configs = () => {
<Heading mb={4}>{translate("config.title")}</Heading>
<Separator />
{error === null ? (
<DataTable columns={columns} data={render} modelName={translate("common:admin.Config")} />
<DataTable columns={columns} data={render} modelName="common:admin.Config" />
) : (
<ErrorAlert error={error} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const Connections = () => {
initialState={tableURLState}
isFetching={isFetching}
isLoading={isLoading}
modelName={translate("common:admin.Connections")}
modelName="admin:connections.connection"
noRowsMessage={<NothingFoundInfo />}
onStateChange={setTableURLState}
total={data?.total_entries ?? 0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const Backfills = () => {
data={data ? data.backfills : []}
isFetching={isFetching}
isLoading={isLoading}
modelName={translate("backfill_one")}
modelName="common:backfill"
onStateChange={setTableURLState}
total={data ? data.total_entries : 0}
/>
Expand Down
4 changes: 1 addition & 3 deletions airflow-core/src/airflow/ui/src/pages/Dag/Tasks/Tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/
import { Skeleton, Box } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { useParams, useSearchParams } from "react-router-dom";

import { useTaskServiceGetTasks } from "openapi/queries";
Expand All @@ -40,7 +39,6 @@ const cardDef = (dagId: string): CardDef<TaskResponse> => ({
export const Tasks = () => {
const { dagId = "" } = useParams();
const { MAPPED, NAME_PATTERN, OPERATOR, RETRIES, TRIGGER_RULE } = SearchParamsKeys;
const { t: translate } = useTranslation();
const [searchParams] = useSearchParams();
const selectedOperators = searchParams.getAll(OPERATOR);
const selectedTriggerRules = searchParams.getAll(TRIGGER_RULE);
Expand Down Expand Up @@ -100,7 +98,7 @@ export const Tasks = () => {
displayMode="card"
isFetching={isFetching}
isLoading={isLoading}
modelName={translate("task_one")}
modelName="common:task"
total={data ? data.total_entries : 0}
/>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export const DagRuns = () => {
errorMessage={<ErrorAlert error={error} />}
initialState={tableURLState}
isLoading={isLoading}
modelName={translate("common:dagRun_other")}
modelName="common:dagRun"
onStateChange={setTableURLState}
total={data?.total_entries}
/>
Expand Down
13 changes: 8 additions & 5 deletions airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import DeleteDagButton from "src/components/DagActions/DeleteDagButton";
import { FavoriteDagButton } from "src/components/DagActions/FavoriteDagButton";
import DagRunInfo from "src/components/DagRunInfo";
import { DataTable } from "src/components/DataTable";
import { ToggleTableDisplay } from "src/components/DataTable/ToggleTableDisplay";
import type { CardDef } from "src/components/DataTable/types";
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
import { ErrorAlert } from "src/components/ErrorAlert";
Expand Down Expand Up @@ -288,6 +287,8 @@ export const DagsList = () => {
[pagination, setTableURLState],
);

const totalEntries = data?.total_entries ?? 0;

return (
<DagsLayout>
<VStack alignItems="none">
Expand All @@ -301,7 +302,7 @@ export const DagsList = () => {
<HStack justifyContent="space-between">
<HStack>
<Heading py={3} size="md">
{`${data?.total_entries ?? 0} ${translate("dag", { count: data?.total_entries ?? 0 })}`}
{`${totalEntries} ${translate("dag", { count: totalEntries })}`}
</Heading>
<DAGImportErrors iconOnly />
</HStack>
Expand All @@ -310,7 +311,6 @@ export const DagsList = () => {
) : undefined}
</HStack>
</VStack>
<ToggleTableDisplay display={display} setDisplay={setDisplay} />
<Box overflow="auto" pb={8}>
<DataTable
cardDef={cardDef}
Expand All @@ -320,10 +320,13 @@ export const DagsList = () => {
errorMessage={<ErrorAlert error={error} />}
initialState={tableURLState}
isLoading={isLoading}
modelName={translate("dag_one")}
modelName="common:dag"
onDisplayToggleChange={setDisplay}
onStateChange={setTableURLState}
showDisplayToggle
showRowCountHeading={false}
skeletonCount={display === "card" ? 5 : undefined}
total={data?.total_entries ?? 0}
total={totalEntries}
/>
</Box>
</DagsLayout>
Expand Down
Loading