FE: Error messages in Storybook (#28640)

This commit is contained in:
RachelElysia
2025-05-06 09:20:03 -04:00
committed by GitHub
parent 57af99785b
commit 5e05fd4dac
49 changed files with 109 additions and 150 deletions
@@ -183,6 +183,7 @@ const DownloadInstallers = ({
/>
);
// TODO: We should be rendering a Flash message instead
if (isDownloadError) {
return (
<div className={`${baseClass}__error`}>
@@ -4,7 +4,7 @@ import { Meta, StoryObj } from "@storybook/react";
import DataError from "./DataError";
const meta: Meta<typeof DataError> = {
title: "Components/DataError",
title: "Components/Error Messages/Data error",
component: DataError,
};
+22 -4
View File
@@ -4,6 +4,7 @@ import classnames from "classnames";
import CustomLink from "components/CustomLink";
import Icon from "components/Icon";
import Graphic from "components/Graphic";
import { Padding } from "styles/var/padding";
const baseClass = "data-error";
@@ -13,7 +14,14 @@ interface IDataErrorProps {
/** Excludes the link that asks user to create an issue. Defaults to `false` */
excludeIssueLink?: boolean;
children?: React.ReactNode;
card?: boolean;
/**
* Sets the vertical padding for the component.
* **Recommended values:**
* - For card-level components, use "pad-large" `24px`.
* - For page-level components, use "pad-xxxlarge"`80px`.
* These values help maintain consistent spacing across the application.
*/
verticalPaddingSize?: Padding;
className?: string;
/** Flag to use the updated DataError design */
useNew?: boolean;
@@ -25,15 +33,21 @@ const DataError = ({
description = DEFAULT_DESCRIPTION,
excludeIssueLink = false,
children,
card,
verticalPaddingSize,
className,
useNew = false,
}: IDataErrorProps): JSX.Element => {
const classes = classnames(baseClass, className);
if (useNew) {
return (
<div className={classes}>
<div className={`${baseClass}__${card ? "card" : "inner-new"}`}>
<div
className={`${baseClass}__inner-new ${
verticalPaddingSize &&
`${baseClass}__vertical-${verticalPaddingSize}`
}`}
>
<Graphic name="data-error" />
<div className={`${baseClass}__header`}>
Something&apos;s gone wrong.
@@ -60,7 +74,11 @@ const DataError = ({
return (
<div className={classes}>
<div className={`${baseClass}__${card ? "card" : "inner"}`}>
<div
className={`${baseClass}__inner ${
verticalPaddingSize && `${baseClass}__vertical-${verticalPaddingSize}`
}`}
>
<div className="info">
<span className="info__header">
<Icon name="error" />
+31 -9
View File
@@ -3,10 +3,28 @@
flex-direction: column;
align-items: center;
&__card {
display: flex;
flex-direction: row;
margin: $pad-large 0 0;
&__vertical-pad-small {
padding: $pad-small 0;
}
&__vertical-pad-medium {
padding: $pad-medium 0;
}
&__vertical-pad-large {
padding: $pad-large 0;
}
&__vertical-pad-xlarge {
padding: $pad-xlarge 0;
}
&__vertical-pad-xxlarge {
padding: $pad-xxlarge 0;
}
&__vertical-pad-xxxlarge {
padding: $pad-xxxlarge 0;
}
&__inner {
@@ -15,6 +33,10 @@
}
.info {
display: flex;
flex-direction: column;
gap: $pad-small;
&__header {
display: flex;
color: $core-fleet-black;
@@ -22,6 +44,7 @@
font-size: $x-small;
text-align: left;
gap: $pad-small;
margin-bottom: $pad-small;
}
&__data {
display: block;
@@ -29,10 +52,9 @@
font-weight: normal;
font-size: $x-small;
text-align: left;
margin-top: 10px;
}
}
// // // // // // // // // // // //
// new version
&__inner-new {
@@ -46,7 +68,7 @@
color: $core-fleet-black;
font-size: $x-small;
}
&__header {
font-weight: $bold;
}
&__header {
font-weight: $bold;
}
}
@@ -3,7 +3,7 @@ import { Meta, StoryObj } from "@storybook/react";
import DeviceUserError from "./DeviceUserError";
const meta: Meta<typeof DeviceUserError> = {
title: "Components/DeviceUserError",
title: "Components/Error messages/Device user error",
component: DeviceUserError,
};
@@ -6,7 +6,7 @@ const baseClass = "device-user-error";
const DeviceUserError = (): JSX.Element => {
return (
<div className={`${baseClass}`}>
<div className={baseClass}>
<div className={`${baseClass}__inner`}>
<div className="info">
<span className="info__header">
@@ -10,8 +10,14 @@
}
.info {
display: flex;
flex-direction: column;
gap: $pad-large;
max-width: 333px;
&__header {
display: block;
gap: $pad-large;
color: $core-fleet-black;
font-weight: $bold;
font-size: $x-small;
@@ -30,7 +36,7 @@
font-weight: normal;
font-size: $x-small;
text-align: center;
margin-top: 10px;
line-height: 21px; // Matches design
}
}
}
@@ -60,6 +60,7 @@ import { CustomOptionType } from "components/forms/fields/DropdownWrapper/Dropdo
import MainContent from "components/MainContent";
import LastUpdatedText from "components/LastUpdatedText";
import Card from "components/Card";
import DataError from "components/DataError";
import {
LOW_DISK_SPACE_GB,
@@ -556,7 +557,11 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
]
);
const HostCountCards = (
const HostCountCards = errorHosts ? (
<Card borderRadiusSize="large">
<DataError verticalPaddingSize="pad-large" />
</Card>
) : (
<>
<PlatformHostCounts
androidDevEnabled={!!config?.android_enabled}
@@ -570,7 +575,6 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
androidCount={androidCount}
builtInLabels={labels}
selectedPlatform={selectedPlatform}
errorHosts={!!errorHosts}
totalHostCount={
!isHostSummaryFetching && !errorHosts
? hostSummaryData?.totals_hosts_count
@@ -580,7 +584,6 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
<MetricsHostCounts
currentTeamId={teamIdForApi}
selectedPlatform={selectedPlatform}
errorHosts={!!errorHosts}
totalHostCount={
!isHostSummaryFetching && !errorHosts
? hostSummaryData?.totals_hosts_count
@@ -3,12 +3,6 @@
overflow: initial; // auto causes double scroll bar but still needed for other pages .main-content div
padding-bottom: 60px; // replaces white padding on off-white page
.data-error {
&__inner {
margin: $pad-xxxlarge 0;
}
}
&__wrapper {
background-color: $ui-off-white;
}
@@ -151,7 +151,7 @@ const ActivityFeed = ({
};
const renderError = () => {
return <DataError card />;
return <DataError verticalPaddingSize="pad-large" />;
};
const renderNoActivities = () => {
@@ -55,36 +55,6 @@
}
}
&__error {
display: flex;
flex-direction: column;
align-items: center;
margin: $pad-xlarge 0;
&__inner {
display: flex;
flex-direction: row;
}
.info {
&__header {
display: block;
color: $core-fleet-black;
font-weight: $bold;
font-size: $x-small;
text-align: left;
}
&__data {
display: block;
color: $core-fleet-black;
font-weight: normal;
font-size: $x-small;
text-align: left;
margin-top: 10px;
}
}
}
.spinner {
z-index: 11;
opacity: 1;
@@ -8,7 +8,7 @@ import TabNav from "components/TabNav";
import TabText from "components/TabText";
import TableContainer from "components/TableContainer";
import Spinner from "components/Spinner";
import TableDataError from "components/DataError";
import DataError from "components/DataError";
import EmptyTable from "components/EmptyTable";
import CustomLink from "components/CustomLink";
@@ -152,7 +152,7 @@ const Mdm = ({
</TabList>
<TabPanel>
{error ? (
<TableDataError card />
<DataError verticalPaddingSize="pad-large" />
) : (
<TableContainer<IRowProps>
className={`${baseClass}__mdm-solutions-table`}
@@ -175,7 +175,7 @@ const Mdm = ({
</TabPanel>
<TabPanel>
{error ? (
<TableDataError card />
<DataError verticalPaddingSize="pad-large" />
) : (
<TableContainer
className={`${baseClass}__mdm-status-table`}
@@ -71,7 +71,7 @@ const Munki = ({
</TabList>
<TabPanel>
{errorMacAdmins ? (
<TableDataError card />
<TableDataError verticalPaddingSize="pad-large" />
) : (
<TableContainer
columnConfigs={tableHeaders}
@@ -98,7 +98,7 @@ const Munki = ({
</TabPanel>
<TabPanel>
{errorMacAdmins ? (
<TableDataError card />
<TableDataError verticalPaddingSize="pad-large" />
) : (
<TableContainer
columnConfigs={munkiVersionsTableHeaders}
@@ -14,7 +14,7 @@ import {
import { PlatformValueOptions } from "utilities/constants";
import Spinner from "components/Spinner";
import TableDataError from "components/DataError";
import DataError from "components/DataError";
import LastUpdatedText from "components/LastUpdatedText";
import CustomLink from "components/CustomLink";
import { AxiosError } from "axios";
@@ -142,7 +142,7 @@ const OperatingSystems = ({
)}
<div style={opacity}>
{error?.status && error?.status >= 500 ? (
<TableDataError card />
<DataError verticalPaddingSize="pad-large" />
) : (
<OSTable
currentTeamId={currentTeamId}
@@ -11,7 +11,7 @@ import { ITableQueryData } from "components/TableContainer/TableContainer";
import TabNav from "components/TabNav";
import TabText from "components/TabText";
import TableContainer from "components/TableContainer";
import TableDataError from "components/DataError";
import DataError from "components/DataError";
import Spinner from "components/Spinner";
import EmptySoftwareTable from "pages/SoftwarePage/components/tables/EmptySoftwareTable";
@@ -90,7 +90,7 @@ const Software = ({
</TabList>
<TabPanel>
{!isSoftwareFetching && errorSoftware ? (
<TableDataError />
<DataError verticalPaddingSize="pad-large" />
) : (
<TableContainer
columnConfigs={tableHeaders}
@@ -113,7 +113,7 @@ const Software = ({
</TabPanel>
<TabPanel>
{!isSoftwareFetching && errorSoftware ? (
<TableDataError />
<DataError verticalPaddingSize="pad-large" />
) : (
<TableContainer
columnConfigs={tableHeaders}
@@ -3,7 +3,6 @@ import React from "react";
import { LOW_DISK_SPACE_GB } from "pages/DashboardPage/helpers";
import { PlatformValueOptions } from "utilities/constants";
import DataError from "components/DataError";
import LowDiskSpaceHosts from "../../cards/LowDiskSpaceHosts";
import MissingHosts from "../../cards/MissingHosts";
import TotalHosts from "../../cards/TotalHosts";
@@ -12,7 +11,6 @@ const baseClass = "metrics-host-counts";
interface IPlatformHostCountsProps {
currentTeamId: number | undefined;
errorHosts: boolean;
selectedPlatform?: PlatformValueOptions;
totalHostCount?: number;
isPremiumTier?: boolean;
@@ -23,7 +21,6 @@ interface IPlatformHostCountsProps {
const MetricsHostCounts = ({
currentTeamId,
errorHosts,
selectedPlatform,
totalHostCount,
isPremiumTier,
@@ -31,10 +28,6 @@ const MetricsHostCounts = ({
lowDiskSpaceCount,
selectedPlatformLabelId,
}: IPlatformHostCountsProps): JSX.Element => {
if (errorHosts) {
return <DataError card />;
}
const TotalHostsCard = (
<TotalHosts
totalCount={totalHostCount}
@@ -7,6 +7,7 @@ import { PLATFORM_NAME_TO_LABEL_NAME } from "pages/DashboardPage/helpers";
import { IHostSummary } from "interfaces/host_summary";
import { PlatformValueOptions } from "utilities/constants";
import DataError from "components/DataError";
import Card from "components/Card";
import HostCountCard from "../../cards/HostCountCard";
@@ -23,7 +24,6 @@ interface IPlatformHostCountsProps {
ipadosCount: number;
androidCount: number;
builtInLabels?: IHostSummary["builtin_labels"];
errorHosts: boolean;
selectedPlatform?: PlatformValueOptions;
totalHostCount?: number;
}
@@ -39,7 +39,6 @@ const PlatformHostCounts = ({
ipadosCount,
androidCount,
builtInLabels,
errorHosts,
selectedPlatform,
totalHostCount,
}: IPlatformHostCountsProps): JSX.Element => {
@@ -252,10 +251,6 @@ const PlatformHostCounts = ({
}
};
if (errorHosts) {
return <DataError card />;
}
return <div className={baseClass}>{renderCounts(currentTeamId)}</div>;
};
@@ -104,7 +104,7 @@ const CurrentVersionSection = ({
if (isError) {
return (
<DataError
className={`${baseClass}__error`}
verticalPaddingSize="pad-xxxlarge"
description="Refresh the page to try again."
excludeIssueLink
/>
@@ -196,7 +196,7 @@ const SoftwareAppStoreVpp = ({
}
if (errorVppInfo || errorVppApps || isErrorLabels) {
return <DataError className={`${baseClass}__error`} />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (noVppTokenUploaded) {
@@ -161,7 +161,7 @@ const SoftwareCustomPackage = ({
}
if (isErrorLabels) {
return <DataError className={`${baseClass}__data-error`} />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
@@ -2,8 +2,4 @@
&__premium-message {
margin-top: $pad-xxxlarge;
}
&__data-error {
margin-top: $pad-xxxlarge;
}
}
@@ -252,7 +252,7 @@ const FleetMaintainedAppDetailsPage = ({
}
if (isErrorFleetApp || isErrorLabels) {
return <DataError className={`${baseClass}__data-error`} />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (fleetApp) {
@@ -1,8 +1,4 @@
.fleet-maintained-app-details-page {
&__data-error {
margin-top: $pad-xxlarge;
}
&__back-to-add-software {
margin-bottom: $pad-medium;
}
@@ -100,7 +100,7 @@ const SoftwareFleetMaintained = ({
}
if (isError) {
return <DataError className={`${baseClass}__table-error`} />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
@@ -1,5 +1,5 @@
.software-fleet-maintained {
&__premium-message, &__table-error {
&__premium-message {
margin-top: $pad-xxxlarge;
}
}
@@ -71,7 +71,7 @@ const SoftwareOS = ({
}
if (isError) {
return <TableDataError className={`${baseClass}__table-error`} />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
@@ -174,7 +174,7 @@ const SoftwareTitles = ({
}
if (isTitlesError || isVersionsError || isTitlesAFIError) {
return <TableDataError className={`${baseClass}__table-error`} />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
@@ -1,5 +0,0 @@
.software-titles {
&__table-error {
margin-top: $pad-xxxlarge;
}
}
@@ -244,7 +244,7 @@ const SoftwareVulnerabilities = ({
}
if (isError) {
return <TableDataError className={`${baseClass}__table-error`} />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
@@ -159,11 +159,7 @@ const AppleBusinessManagerPage = ({ router }: { router: InjectedRouter }) => {
// TODO: error UI
if (showDataError) {
return (
<div>
<DataError />
</div>
);
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (abmTokens?.length === 0) {
@@ -100,7 +100,7 @@ const AppleMdmPage = ({ router }: { router: InjectedRouter }) => {
/>
<h1>Apple Push Certificate Portal</h1>
{showSpinner && <Spinner />}
{showError && <DataError />}
{showError && <DataError verticalPaddingSize="pad-xxxlarge" />}
{showContent &&
(!appleAPNInfo ? (
<ApplePushCertSetup
@@ -154,11 +154,7 @@ const VppPage = ({ router }: IVppPageProps) => {
// TODO: error UI
if (showDataError) {
return (
<div>
<DataError />
</div>
);
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (vppTokens?.length === 0) {
@@ -283,7 +283,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
const renderForm = () => {
if (errorLoadGlobalConfig || errorLoadTeamConfig) {
return <DataError />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (isLoadingTeamConfig || isLoadingAppConfig) {
return <Spinner />;
@@ -397,7 +397,7 @@ const UsersPage = ({ location, router }: ITeamSubnavProps): JSX.Element => {
{loadingUsersError ||
loadingTeamsError ||
(!currentTeamDetails && !isLoadingTeams && !isLoadingUsers) ? (
<TableDataError />
<TableDataError verticalPaddingSize="pad-xxxlarge" />
) : (
<TableContainer
resultsTitle="users"
@@ -554,7 +554,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
return (
<>
{tableDataError ? (
<TableDataError />
<TableDataError verticalPaddingSize="pad-xxxlarge" />
) : (
<TableContainer
columnConfigs={tableHeaders}
@@ -80,7 +80,7 @@ import TableContainer from "components/TableContainer";
import InfoBanner from "components/InfoBanner/InfoBanner";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import TableDataError from "components/DataError";
import DataError from "components/DataError";
import { IActionButtonProps } from "components/TableContainer/DataTable/ActionButton/ActionButton";
import TeamsDropdown from "components/TeamsDropdown";
import Spinner from "components/Spinner";
@@ -1485,7 +1485,7 @@ const ManageHostsPage = ({
}
if (hasErrors) {
return <TableDataError />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (maybeEmptyHosts) {
const emptyState = () => {
@@ -229,7 +229,7 @@ const DeviceUserPage = ({
const {
data: dupResponse,
isLoading: isLoadingHost,
error: loadingDeviceUserError,
error: isDeviceUserError,
refetch: refetchHostDetails,
} = useQuery<IDeviceUserResponse, Error>(
["host", deviceAuthToken],
@@ -709,7 +709,7 @@ const DeviceUserPage = ({
</ul>
</div>
</nav>
{loadingDeviceUserError ? <DeviceUserError /> : renderDeviceUserPage()}
{isDeviceUserError ? <DeviceUserError /> : renderDeviceUserPage()}
</div>
);
};
@@ -48,18 +48,6 @@
}
}
&__error {
display: flex;
flex-direction: column;
align-items: center;
margin: $pad-xlarge 0;
&__inner {
display: flex;
flex-direction: row;
}
}
&__wrapper {
border: solid 1px $ui-fleet-black-10;
border-radius: 6px;
@@ -29,7 +29,7 @@ const PastActivityFeed = ({
onPreviousPage,
}: IPastActivityFeedProps) => {
if (isError) {
return <DataError className={`${baseClass}__error`} />;
return <DataError verticalPaddingSize="pad-large" />;
}
if (!activities) {
@@ -36,8 +36,4 @@
background-color: $ui-vibrant-blue-10;
}
}
&__error {
margin: $pad-xlarge 0;
}
}
@@ -32,7 +32,7 @@ const UpcomingActivityFeed = ({
onPreviousPage,
}: IUpcomingActivityFeedProps) => {
if (isError) {
return <DataError />;
return <DataError verticalPaddingSize="pad-large" />;
}
if (!activities) {
@@ -36,8 +36,4 @@
background-color: $ui-vibrant-blue-10;
}
}
&__error {
margin: $pad-xlarge 0;
}
}
@@ -47,7 +47,7 @@ const CertificatesCard = ({
onSortChange,
}: ICertificatesProps) => {
const renderContent = () => {
if (isError) return <DataError />;
if (isError) return <DataError verticalPaddingSize="pad-large" />;
return (
<CertificatesTable
@@ -381,7 +381,7 @@ const HostSoftware = ({
}
return (
<>
{isError && <DataError />}
{isError && <DataError verticalPaddingSize="pad-xxxlarge" />}
{!isError && (
<HostSoftwareTable
isLoading={
@@ -312,7 +312,7 @@ const SoftwareSelfService = ({
}
if (isError) {
return <DataError />;
return <DataError verticalPaddingSize="pad-xxxlarge" />;
}
if (isEmpty || !selfServiceData) {
@@ -968,7 +968,7 @@ const ManagePolicyPage = ({
// Global policies
if (globalPoliciesError) {
return <TableDataError />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
<PoliciesTable
@@ -998,7 +998,7 @@ const ManagePolicyPage = ({
// Team policies
if (teamPoliciesError) {
return <TableDataError />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
<div>
@@ -281,7 +281,7 @@ const ManageQueriesPage = ({
const renderQueriesTable = () => {
if (queriesError) {
return <TableDataError />;
return <TableDataError verticalPaddingSize="pad-xxxlarge" />;
}
return (
<QueriesTable
@@ -401,12 +401,10 @@ const QueryDetailsPage = ({
disabledCachingGlobally || lastEditedQueryDiscardData || !loggingSnapshot;
const emptyCache = (queryReport?.results?.length ?? 0) === 0;
// Loading state
if (isLoading) {
return <Spinner />;
}
// Error state
if (isApiError) {
return <DataError />;
}