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 @@ -192,6 +192,10 @@ const tests: ComponentTest[] = [
}),
_yext: { contentDeliveryAPIDomain: "https://cdn.yextapis.com" },
c_nearbyHeader: "Nearby",
yextDisplayCoordinate: {
latitude: 25.73398,
longitude: -80.319968,
},
},
props: {
data: {
Expand Down Expand Up @@ -361,6 +365,10 @@ const tests: ComponentTest[] = [
}),
_yext: { contentDeliveryAPIDomain: "https://cdn.yextapis.com" },
c_nearbyHeader: "Nearby",
yextDisplayCoordinate: {
latitude: 25.73398,
longitude: -80.319968,
},
},
props: {
styles: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,9 @@ import {
VisibilityWrapper,
msg,
HeadingTextProps,
Body,
pt,
} from "@yext/visual-editor";
import { AnalyticsScopeProvider } from "@yext/pages-components";
import {
defaultNearbyLocationsCardsProps,
NearbyLocationCardsWrapperProps,
} from "./NearbyLocationsCardsWrapper";
import { MapPinOff } from "lucide-react";
import { useTemplateMetadata } from "../../../internal/hooks/useMessageReceivers";
import { defaultNearbyLocationsCardsProps } from "./NearbyLocationsCardsWrapper";

export interface NearbyLocationsSectionProps {
/**
Expand Down Expand Up @@ -99,63 +92,16 @@ const nearbyLocationsSectionFields: Fields<NearbyLocationsSectionProps> = {
),
};

/** @internal */
const NearbyLocationsEmptyState: React.FC<{
backgroundColor?: BackgroundStyle;
radius?: number;
}> = ({ backgroundColor, radius }) => {
const templateMetadata = useTemplateMetadata();
const entityTypeDisplayName =
templateMetadata?.entityTypeDisplayName?.toLowerCase();

return (
<PageSection background={backgroundColor}>
<div className="relative h-[300px] w-full bg-gray-100 rounded-lg border border-gray-200 flex flex-col items-center justify-center py-8 gap-2.5">
<MapPinOff className="w-12 h-12 text-gray-400" />
<div className="flex flex-col items-center gap-0">
<Body variant="base" className="text-gray-500 font-medium">
{pt(
"nearbyLocationsEmptyStateSectionHidden",
"Section hidden for this {{entityType}}",
{
entityType: entityTypeDisplayName
? entityTypeDisplayName
: "page",
}
)}
</Body>
<Body variant="base" className="text-gray-500 font-normal">
{pt(
"nearbyLocationsEmptyState",
"No {{entityType}} within {{radius}} miles",
{
entityType: entityTypeDisplayName
? entityTypeDisplayName
: "entity",
radius: radius ?? 10,
}
)}
</Body>
</div>
</div>
</PageSection>
);
};

const NearbyLocationsComponent: PuckComponent<NearbyLocationsSectionProps> = (
props
) => {
const { styles, slots, puck } = props;
const cardsWrapperRef = React.useRef<HTMLDivElement>(null);
const [showSection, setShowSection] = React.useState<boolean>(true);
const [isEmptyState, setIsEmptyState] = React.useState<boolean>(false);

// Get cards wrapper props to access radius for empty state
const cardsWrapperSlot = slots.CardsWrapperSlot;
const cardsWrapperProps =
Array.isArray(cardsWrapperSlot) && cardsWrapperSlot[0]
? (cardsWrapperSlot[0].props as NearbyLocationCardsWrapperProps)
: undefined;
// Hide the header if there are no nearby locations and it is the editor
const [showHeading, setShowHeading] = React.useState<boolean>(true);
// Hide the entire section if there are no nearby locations and it is the live page
const [hideEntireSection, setHideEntireSection] =
React.useState<boolean>(false);

React.useEffect(() => {
// Watch the cards wrapper element to see if any cards are rendered
Expand All @@ -172,8 +118,8 @@ const NearbyLocationsComponent: PuckComponent<NearbyLocationsSectionProps> = (
const isLoading = element.querySelector('[data-loading="true"]') !== null; // Check for loading state
const shouldShow =
hasContent || isLoading || (hasHeight && !hasEmptyStateMarker);
setShowSection(shouldShow);
setIsEmptyState(hasEmptyStateMarker && puck.isEditing);
setShowHeading(shouldShow);
setHideEntireSection(hasEmptyStateMarker && !puck.isEditing);
};

const observer = new ResizeObserver(() => {
Expand Down Expand Up @@ -201,22 +147,14 @@ const NearbyLocationsComponent: PuckComponent<NearbyLocationsSectionProps> = (
}, [puck.isEditing]);

// Show empty state if detected
if (isEmptyState) {
return (
<NearbyLocationsEmptyState
backgroundColor={styles?.backgroundColor}
radius={cardsWrapperProps?.data?.radius}
/>
);
if (hideEntireSection) {
return <></>;
}

return (
<PageSection
background={styles?.backgroundColor}
outerClassName={showSection ? undefined : "p-0 m-0"}
>
<PageSection background={styles?.backgroundColor}>
<div className="space-y-6">
{showSection && (
{showHeading && (
<slots.SectionHeadingSlot style={{ height: "auto" }} allow={[]} />
)}
<div ref={cardsWrapperRef}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,25 @@ import * as React from "react";
import { useTranslation } from "react-i18next";
import { ComponentConfig, Fields, PuckComponent } from "@measured/puck";
import { useQuery } from "@tanstack/react-query";
import { Coordinate } from "@yext/pages-components";
import {
backgroundColors,
BackgroundStyle,
Body,
HeadingLevel,
msg,
pt,
resolveComponentData,
useDocument,
YextEntityField,
YextField,
} from "@yext/visual-editor";
import { parseDocument, fetchNearbyLocations } from "./utils";
import { NearbyLocationCard } from "./NearbyLocationCard";
import { useTemplateMetadata } from "../../../internal/hooks/useMessageReceivers";
import { MapPinOff } from "lucide-react";

export type NearbyLocationCardsWrapperProps = {
/** The search parameters for finding nearby locations. */
data: {
/**
* The central coordinate (`latitude`, `longitude`) to search from.
* @defaultValue 'yextDisplayCoordinate' field
*/
coordinate: YextEntityField<Coordinate>;

/**
* The search radius in miles.
* @defaultValue 10
Expand Down Expand Up @@ -82,13 +77,6 @@ const nearbyLocationCardsWrapperFields: Fields<NearbyLocationCardsWrapperProps>
data: YextField(msg("fields.data", "Data"), {
type: "object",
objectFields: {
coordinate: YextField<any, Coordinate>(
msg("fields.coordinates", "Coordinates"),
{
type: "entityField",
filter: { types: ["type.coordinate"] },
}
),
radius: YextField(msg("fields.radiusMiles", "Radius (Miles)"), {
type: "number",
min: 0,
Expand Down Expand Up @@ -204,7 +192,14 @@ const NearbyLocationCardsWrapperComponent: PuckComponent<
const locale = i18n.language;

const coordinate = resolveComponentData(
data?.coordinate,
{
field: "yextDisplayCoordinate",
constantValue: {
latitude: 0,
longitude: 0,
},
constantValueEnabled: false,
},
locale,
streamDocument
);
Expand Down Expand Up @@ -279,13 +274,14 @@ const NearbyLocationCardsWrapperComponent: PuckComponent<
);
}

// do not render the component if there's no data or it's not enabled
// Render an empty state if no nearby locations are found
// The parent component will hide the entire section if it is the live page
if (!enableNearbyLocations || !nearbyLocationsData?.response?.docs?.length) {
// Return a marker element so parent can detect empty state
if (puck.isEditing) {
return <div data-empty-state="true" style={{ display: "none" }} />;
return <NearbyLocationsEmptyState radius={data.radius} />;
} else {
return <div data-empty-state="true" />;
}
return <></>;
}

return (
Expand Down Expand Up @@ -317,13 +313,6 @@ const NearbyLocationCardsWrapperComponent: PuckComponent<
export const defaultNearbyLocationsCardsProps: NearbyLocationCardsWrapperProps =
{
data: {
coordinate: {
field: "yextDisplayCoordinate",
constantValue: {
latitude: 0,
longitude: 0,
},
},
radius: 10,
limit: 3,
},
Expand Down Expand Up @@ -351,3 +340,46 @@ export const NearbyLocationCardsWrapper: ComponentConfig<{
defaultProps: defaultNearbyLocationsCardsProps,
render: (props) => <NearbyLocationCardsWrapperComponent {...props} />,
};

/** @internal */
const NearbyLocationsEmptyState: React.FC<{
radius?: number;
}> = ({ radius }) => {
const templateMetadata = useTemplateMetadata();
const entityTypeDisplayName =
templateMetadata?.entityTypeDisplayName?.toLowerCase();

return (
<div
data-empty-state="true"
className="relative h-[300px] w-full bg-gray-100 rounded-lg border border-gray-200 flex flex-col items-center justify-center py-8 gap-2.5"
>
<MapPinOff className="w-12 h-12 text-gray-400" />
<div className="flex flex-col items-center gap-0">
<Body variant="base" className="text-gray-500 font-medium">
{pt(
"nearbyLocationsEmptyStateSectionHidden",
"Section hidden for this {{entityType}}",
{
entityType: entityTypeDisplayName
? entityTypeDisplayName
: "page",
}
)}
</Body>
<Body variant="base" className="text-gray-500 font-normal">
{pt(
"nearbyLocationsEmptyState",
"No {{entityType}} within {{radius}} miles",
{
entityType: entityTypeDisplayName
? entityTypeDisplayName
: "entity",
radius: radius ?? 10,
}
)}
</Body>
</div>
</div>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 1 addition & 5 deletions packages/visual-editor/src/vite-plugin/defaultLayoutData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const mainDefaultLayout = {
root: {
props: {
version: 41,
version: 46,
title: { field: "name", constantValue: "", constantValueEnabled: false },
description: {
field: "description",
Expand Down Expand Up @@ -3376,10 +3376,6 @@ const mainDefaultLayout = {
props: {
id: "NearbyLocationCardsWrapper-7d43eba1-9a72-49eb-876d-3b77d672198b",
data: {
coordinate: {
field: "yextDisplayCoordinate",
constantValue: { latitude: 0, longitude: 0 },
},
radius: 10,
limit: 3,
},
Expand Down