Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: EmptyState component #2175

Merged
merged 12 commits into from
Apr 30, 2024
6 changes: 3 additions & 3 deletions packages/odyssey-react-mui/src/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
} from "../OdysseyDesignTokensContext";
import { useScrollIndication } from "./useScrollIndication";
import styled from "@emotion/styled";
import { DataTableEmptyState } from "./DataTableEmptyState";
import { EmptyState } from "../EmptyState";
import { Callout } from "../Callout";
import { t } from "i18next";

Expand Down Expand Up @@ -573,9 +573,9 @@ const DataTable = ({

const emptyState = useCallback(() => {
const noResultsInnerContent = noResultsPlaceholder || (
<DataTableEmptyState
<EmptyState
jordankoschei-okta marked this conversation as resolved.
Show resolved Hide resolved
heading={t("table.noresults.heading")}
text={t("table.noresults.text")}
description={t("table.noresults.text")}
/>
);

Expand Down
1 change: 0 additions & 1 deletion packages/odyssey-react-mui/src/DataTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export {
type DataTableOnReorderRowsType,
type DataTableRenderDetailPanelType,
} from "./DataTable";
export { DataTableEmptyState } from "./DataTableEmptyState";
export { densityValues } from "./constants";
export type {
MRT_ColumnFiltersState as DataTableFiltersState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
*/

import { ReactNode, memo } from "react";
import { Heading4, Paragraph } from "../Typography";
import { Box } from "../Box";
import { Heading4, Paragraph } from "./Typography";
import { Box } from "./Box";
import styled from "@emotion/styled";
import {
useOdysseyDesignTokens,
DesignTokens,
} from "../OdysseyDesignTokensContext";
} from "./OdysseyDesignTokensContext";

const EmptyContainer = styled("div", {
shouldForwardProp: (prop) => prop !== "odysseyDesignTokens",
Expand All @@ -28,37 +28,51 @@ const EmptyContainer = styled("div", {
padding: odysseyDesignTokens.Spacing5,
textAlign: "center",
width: "100%",
alignItems: "center",
}));

export type DataTableEmptyStateProps = {
export type EmptyStateProps = {
/**
* Main heading of the empty state
*/
heading: string;
text: string;
primaryButton?: ReactNode;
secondaryButton?: ReactNode;
/**
* A descriptive text explaining more context as to why we don't have data.
*/
description: string;
/**
* Primary call to action
*/
PrimaryCallToActionComponent?: ReactNode;
/**
* Secondary call to action
*/
SecondaryCallToActionComponent?: ReactNode;
};

const DataTableEmptyState = ({
const EmptyState = ({
heading,
text,
primaryButton,
secondaryButton,
}: DataTableEmptyStateProps) => {
description,
PrimaryCallToActionComponent,
SecondaryCallToActionComponent,
}: EmptyStateProps) => {
const odysseyDesignTokens = useOdysseyDesignTokens();

return (
<EmptyContainer odysseyDesignTokens={odysseyDesignTokens}>
<Heading4>{heading}</Heading4>
<Paragraph>{text}</Paragraph>
{(primaryButton || secondaryButton) && (
<Paragraph>{description}</Paragraph>
{(PrimaryCallToActionComponent || SecondaryCallToActionComponent) && (
<Box sx={{ marginBlockStart: 5 }}>
{secondaryButton}
{primaryButton}
{SecondaryCallToActionComponent}
{PrimaryCallToActionComponent}
</Box>
)}
</EmptyContainer>
);
};

const MemoizedDataTableEmptyState = memo(DataTableEmptyState);
MemoizedDataTableEmptyState.displayName = "DataTableEmptyState";
const MemoizedEmptyState = memo(EmptyState);
MemoizedEmptyState.displayName = "EmptyState";

export { MemoizedDataTableEmptyState as DataTableEmptyState };
export { MemoizedEmptyState as EmptyState };
1 change: 1 addition & 0 deletions packages/odyssey-react-mui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export * from "./createUniqueId";
export * from "./DataTable";
export * from "./Dialog";
export * from "./Drawer";
export * from "./EmptyState";
export * from "./Fieldset";
export * from "./FieldComponentProps";
export * from "./Form";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,10 @@ There's a default "no results" state, which shows when the end user passes a que
the user filters the table for the name "asdfadsfadsfasdfdsfd" and that name doesn't exist in the data, the "no response" state
will be shown.)

You can provide a custom "no response" state using the custom `<DataTableEmptyState>` component:
You can provide a custom "no response" state using the custom `<EmptyState>` component:

```tsx
noResultsPlaceholder={<DataTableEmptyState
noResultsPlaceholder={<EmptyState
heading="Whoops, there's nothing here!"
text="You should try searching or filtering for something else."
/>}
Expand All @@ -379,11 +379,11 @@ for providing a first-run experience for the DataTable:

```tsx
emptyPlaceholder={
<DataTableEmptyState
<EmptyState
heading="Start by adding data assets"
text="All relevant data will be displayed and can be searched and filtered"
primaryButton={<Button variant="primary" label="Primary" />}
secondaryButton={<Button variant="secondary" label="Secondary" />}
PrimaryCallToActionComponent={<Button variant="primary" label="Primary" />}
SecondaryCallToActionComponent={<Button variant="secondary" label="Secondary" />}
/>
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
Box,
Button,
DataTable,
DataTableEmptyState,
EmptyState,
DataTableGetDataType,
DataTableOnReorderRowsType,
DataTableProps,
Expand Down Expand Up @@ -291,7 +291,7 @@ const storybookMeta: Meta<DataTableProps> = {
"The component to display when the table is displaying the initial empty state.",
table: {
type: {
summary: `ReactElement<typeof DataTableEmptyState>`,
summary: `ReactElement<typeof EmptyState>`,
},
},
},
Expand All @@ -301,7 +301,7 @@ const storybookMeta: Meta<DataTableProps> = {
"The component to display when the query returns no results.",
table: {
type: {
summary: `ReactElement<typeof DataTableEmptyState>`,
summary: `ReactElement<typeof EmptyState>`,
},
},
},
Expand Down Expand Up @@ -513,21 +513,25 @@ export const API: StoryObj<DataTableProps> = {

const emptyPlaceholder = useMemo(
() => (
<DataTableEmptyState
<EmptyState
heading="Start by adding data assets"
text="All relevant data will be displayed and can be searched and filtered"
primaryButton={<Button variant="primary" label="Primary" />}
secondaryButton={<Button variant="secondary" label="Secondary" />}
description="All relevant data will be displayed and can be searched and filtered"
PrimaryCallToActionComponent={
<Button variant="primary" label="Primary" />
}
SecondaryCallToActionComponent={
<Button variant="secondary" label="Secondary" />
}
/>
),
[],
);

const noResultsPlaceholder = useMemo(
() => (
<DataTableEmptyState
<EmptyState
heading="Whoops, there's nothing here!"
text="You should try searching or filtering for something else."
description="You should try searching or filtering for something else."
/>
),
[],
Expand Down Expand Up @@ -590,11 +594,15 @@ export const Empty: StoryObj<DataTableProps> = {

const emptyPlaceholder = useMemo(
() => (
<DataTableEmptyState
<EmptyState
heading="Start by adding data assets"
text="All relevant data will be displayed and can be searched and filtered"
primaryButton={<Button variant="primary" label="Primary" />}
secondaryButton={<Button variant="secondary" label="Secondary" />}
description="All relevant data will be displayed and can be searched and filtered"
PrimaryCallToActionComponent={
<Button variant="primary" label="Primary" />
}
SecondaryCallToActionComponent={
<Button variant="secondary" label="Secondary" />
}
/>
),
[],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*!
* Copyright (c) 2021-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

import { Meta, StoryObj } from "@storybook/react";
import { Button, EmptyState, EmptyStateProps } from "@okta/odyssey-react-mui";
import { MuiThemeDecorator } from "../../../../.storybook/components";

const storyBookMeta: Meta<EmptyStateProps> = {
title: "MUI Components/EmptyState",
component: EmptyState,
argTypes: {
heading: {
control: "text",
description: "Main heading of the empty state",
type: {
required: true,
name: "string",
},
},

description: {
control: "text",
description:
"A descriptive text explaining more context as to why we don't have data",
type: {
required: true,
name: "string",
},
},

PrimaryCallToActionComponent: {
description: "Primary call to action",
control: "custom",
},

SecondaryCallToActionComponent: {
description: "Secondary call to action",
control: "custom",
},
},
decorators: [MuiThemeDecorator],
tags: ["autodocs"],
};

export default storyBookMeta;

export const Default: StoryObj<EmptyStateProps> = {
args: {
heading: "Start by adding data assets",
description:
"All relevant data will be displayed and can be searched and filtered",
PrimaryCallToActionComponent: (
<Button label="Button label" variant="primary" />
),
SecondaryCallToActionComponent: (
<Button label="Button label" variant="secondary" />
),
},

render: function C(props) {
return <EmptyState {...props} />;
},
};
Loading