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
12 changes: 12 additions & 0 deletions src/visualBuilder/components/VariantIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "preact/compat";
import { VariantIcon } from "./icons/variant";
import { visualBuilderStyles } from "../visualBuilder.style";

export function VariantIndicator(): JSX.Element {
return (
<div className={visualBuilderStyles()["visual-builder__variant-indicator"]}>
<VariantIcon size="18px" />
</div>
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ vi.mock("../generators/generateCustomCursor", () => ({
}));

vi.mock("../visualBuilder.style", () => ({
visualBuilderStyles: vi.fn().mockReturnValue({}),
visualBuilderStyles: vi.fn().mockReturnValue({
"visual-builder__focused-toolbar--variant": "visual-builder__focused-toolbar--variant"
}),
}));

vi.mock("../VariantIndicator", () => ({
VariantIndicator: () => <div data-testid="variant-indicator">Variant</div>
}));

vi.mock("../../utils/errorHandling", () => ({
Expand Down Expand Up @@ -387,4 +393,78 @@ describe("FieldLabelWrapperComponent", () => {
const contentTypeIcon = container.querySelector(".visual-builder__content-type-icon");
expect(contentTypeIcon).not.toBeInTheDocument();
});

test("renders VariantIndicator when field has variant", async () => {
const variantFieldMetadata = {
...mockFieldMetadata,
variant: "variant-uid-123"
};

const { findByTestId } = await asyncRender(
<FieldLabelWrapperComponent
fieldMetadata={variantFieldMetadata}
eventDetails={mockEventDetails}
parentPaths={[]}
getParentEditableElement={mockGetParentEditable}
/>
);

const variantIndicator = await findByTestId("variant-indicator");
expect(variantIndicator).toBeInTheDocument();
});

test("does not render VariantIndicator when field has no variant", async () => {
const { container } = await asyncRender(
<FieldLabelWrapperComponent
fieldMetadata={mockFieldMetadata}
eventDetails={mockEventDetails}
parentPaths={[]}
getParentEditableElement={mockGetParentEditable}
/>
);

await waitFor(() => {
const variantIndicator = container.querySelector("[data-testid='variant-indicator']");
expect(variantIndicator).not.toBeInTheDocument();
});
});

test("applies variant CSS classes when field has variant", async () => {
const variantFieldMetadata = {
...mockFieldMetadata,
variant: "variant-uid-123"
};

const { findByTestId } = await asyncRender(
<FieldLabelWrapperComponent
fieldMetadata={variantFieldMetadata}
eventDetails={mockEventDetails}
parentPaths={[]}
getParentEditableElement={mockGetParentEditable}
/>
);

const fieldLabelWrapper = await findByTestId("visual-builder__focused-toolbar__field-label-wrapper");

await waitFor(() => {
expect(fieldLabelWrapper).toHaveClass("visual-builder__focused-toolbar--variant");
});
});

test("does not apply variant CSS classes when field has no variant", async () => {
const { findByTestId } = await asyncRender(
<FieldLabelWrapperComponent
fieldMetadata={mockFieldMetadata}
eventDetails={mockEventDetails}
parentPaths={[]}
getParentEditableElement={mockGetParentEditable}
/>
);

const fieldLabelWrapper = await findByTestId("visual-builder__focused-toolbar__field-label-wrapper");

await waitFor(() => {
expect(fieldLabelWrapper).not.toHaveClass("visual-builder__focused-toolbar--variant");
});
});
});
11 changes: 11 additions & 0 deletions src/visualBuilder/components/fieldLabelWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types
import { ContentTypeIcon } from "./icons";
import { ToolbarTooltip } from "./Tooltip";
import { fetchEntryPermissionsAndStageDetails } from "../utils/fetchEntryPermissionsAndStageDetails";
import { VariantIndicator } from "./VariantIndicator";

interface ReferenceParentMap {
[entryUid: string]: {
Expand Down Expand Up @@ -246,6 +247,7 @@ function FieldLabelWrapperComponent(
]
)}
>
{currentField.isVariant ? <VariantIndicator /> : null}
<ToolbarTooltip data={{contentTypeName: currentField.parentContentTypeName, referenceFieldName: currentField.referenceFieldName}} disabled={!currentField.isReference || isDropdownOpen}>
<div
className={classNames(
Expand All @@ -266,6 +268,15 @@ function FieldLabelWrapperComponent(
"field-label-dropdown-open": isDropdownOpen,
[visualBuilderStyles()["field-label-dropdown-open"]]:
isDropdownOpen,
},
{
"visual-builder__focused-toolbar--variant":
currentField.isVariant,
},
{
[visualBuilderStyles()[
"visual-builder__focused-toolbar--variant"
]]: currentField.isVariant,
}
)}
onClick={() => setIsDropdownOpen((prev) => !prev)}
Expand Down
12 changes: 9 additions & 3 deletions src/visualBuilder/components/icons/variant.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import React from "preact/compat";

export function VariantIcon(): JSX.Element {
return (
export function VariantIcon(props: {
size?: string;
}): JSX.Element {
return (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{
width: props.size,
height: props.size,
}}
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.41131 0.157165C4.34585 0.0589769 4.23565 0 4.11765 0C3.99964 0 3.88944 0.0589769 3.82398 0.157165L0.0592764 5.80422C-0.0197588 5.92278 -0.0197588 6.07722 0.0592764 6.19578L3.82398 11.8428C3.88944 11.941 3.99964 12 4.11765 12C4.23565 12 4.34585 11.941 4.41131 11.8428L6 9.4598L7.58869 11.8428C7.65415 11.941 7.76435 12 7.88235 12C8.00036 12 8.11056 11.941 8.17602 11.8428L11.9407 6.19578C12.0198 6.07722 12.0198 5.92278 11.9407 5.80422L8.17602 0.157165C8.11056 0.0589769 8.00036 0 7.88235 0C7.76435 0 7.65415 0.0589769 7.58869 0.157165L6 2.5402L4.41131 0.157165ZM5.57582 3.17647L4.11765 0.989215L0.777124 6L4.11765 11.0108L5.57582 8.82353L3.82398 6.19578C3.74495 6.07722 3.74495 5.92278 3.82398 5.80422L5.57582 3.17647ZM6 8.18726L4.54183 6L6 3.81274L7.45817 6L6 8.18726ZM6.42418 8.82353L8.17602 6.19578C8.25505 6.07722 8.25505 5.92278 8.17602 5.80422L6.42418 3.17647L7.88235 0.989215L11.2229 6L7.88235 11.0108L6.42418 8.82353Z"
fill="currentColor"
/>
</svg>
</svg>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe("appendFieldPathDropdown", () => {
fireEvent.click(focusedToolbar);

expect(fieldLabelWrapper?.classList.toString()).toBe(
"visual-builder__focused-toolbar__field-label-wrapper go3399023040"
"visual-builder__focused-toolbar__field-label-wrapper go3061601331"
);
});
});
12 changes: 11 additions & 1 deletion src/visualBuilder/generators/generateHoverOutline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { visualBuilderStyles } from "../visualBuilder.style";
*/
export function addHoverOutline(
targetElement: Element,
disabled?: boolean
disabled?: boolean,
isVariant?: boolean
): void {
const targetElementDimension = targetElement.getBoundingClientRect();

Expand All @@ -29,6 +30,15 @@ export function addHoverOutline(
hoverOutline.classList.remove(
visualBuilderStyles()["visual-builder__hover-outline--disabled"]
);
if (isVariant) {
hoverOutline.classList.add(
visualBuilderStyles()["visual-builder__hover-outline--variant"]
);
} else {
hoverOutline.classList.remove(
visualBuilderStyles()["visual-builder__hover-outline--variant"]
);
}
}

hoverOutline.style.top = `${
Expand Down
24 changes: 13 additions & 11 deletions src/visualBuilder/listeners/mouseHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ async function addOutline(params?: AddOutlineParams): Promise<void> {
fieldDisabled,
} = params;
if (!editableElement) return;
addHoverOutline(editableElement as HTMLElement, fieldDisabled);
const isVariant = !!fieldMetadata.variant;
addHoverOutline(editableElement as HTMLElement, fieldDisabled, isVariant);
const fieldSchema = await FieldSchemaMap.getFieldSchema(
content_type_uid,
fieldPath
Expand All @@ -100,7 +101,7 @@ async function addOutline(params?: AddOutlineParams): Promise<void> {
entryAcl,
entryWorkflowStageDetails
);
addHoverOutline(editableElement, fieldDisabled || isDisabled);
addHoverOutline(editableElement, fieldDisabled || isDisabled, isVariant);
}

const debouncedAddOutline = debounce(addOutline, 50, { trailing: true });
Expand Down Expand Up @@ -333,15 +334,16 @@ const throttledMouseHover = throttle(async (params: HandleMouseHoverParams) => {
const isFocussed= VisualBuilder.VisualBuilderGlobalState.value.isFocussed;
if(!isFocussed) {
showHoverToolbar({
event: params.event,
overlayWrapper: params.overlayWrapper,
visualBuilderContainer: params.visualBuilderContainer,
previousSelectedEditableDOM:
VisualBuilder.VisualBuilderGlobalState.value
.previousSelectedEditableDOM,
focusedToolbar: params.focusedToolbar,
resizeObserver: params.resizeObserver,
});
event: params.event,
overlayWrapper: params.overlayWrapper,
visualBuilderContainer: params.visualBuilderContainer,
previousSelectedEditableDOM:
VisualBuilder.VisualBuilderGlobalState.value
.previousSelectedEditableDOM,
focusedToolbar: params.focusedToolbar,
resizeObserver: params.resizeObserver,
}
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/visualBuilder/utils/__test__/isFieldDisabled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ describe("isFieldDisabled", () => {
const result = isFieldDisabled(fieldSchemaMap, eventFieldDetails);
expect(result.isDisabled).toBe(true);
expect(result.reason).toBe(
"Open an Experience from Audience widget to start editing"
"To edit an experience, open the Audience widget and click the Edit icon."
);
});

Expand Down
2 changes: 1 addition & 1 deletion src/visualBuilder/utils/isFieldDisabled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const DisableReason = {
LocalizedEntry: "Editing this field is restricted in localized entries",
UnlinkedVariant:
"This field is not editable as it is not linked to the selected variant",
AudienceMode: "Open an Experience from Audience widget to start editing",
AudienceMode: "To edit an experience, open the Audience widget and click the Edit icon.",
DisabledVariant:
"This field is not editable as it doesn't match the selected variant",
UnlocalizedVariant: "This field is not editable as it is not localized",
Expand Down
27 changes: 26 additions & 1 deletion src/visualBuilder/visualBuilder.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,21 @@ export function visualBuilderStyles() {
"visual-builder__cursor-icon--loader": css`
animation: visual-builder__spinner 1s linear infinite;
`,
"visual-builder__variant-indicator": css`
height: calc(100% - 1px);
aspect-ratio: 1;
background: white;
border-radius: 2px;
border-width: 2px;
border-style: solid;
align-content: center;
text-align: center;
border-color: #BD59FA;

svg {
color: #BD59FA;
}
`,
"visual-builder__focused-toolbar": css`
position: absolute;
transform: translateY(-100%);
Expand Down Expand Up @@ -347,10 +362,11 @@ export function visualBuilderStyles() {
display: flex;
flex-direction: column-reverse;
position: relative;
margin-right: 0.5rem;
`,
"visual-builder__focused-toolbar__field-label-container": css`
display: flex;
column-gap: 0.5rem;
height: 1.75rem;
align-items: center;
`,
"visual-builder__button": css`
Expand Down Expand Up @@ -514,6 +530,12 @@ export function visualBuilderStyles() {
background: #909090;
}
`,
"visual-builder__focused-toolbar--variant": css`
.visual-builder__focused-toolbar__field-label-wrapper__current-field {
background: #BD59FA;
}

`,
"visual-builder__cursor-disabled": css`
.visual-builder__cursor-icon {
background: #909090;
Expand Down Expand Up @@ -612,6 +634,9 @@ export function visualBuilderStyles() {
"visual-builder__hover-outline--disabled": css`
outline: 2px dashed #909090;
`,
"visual-builder__hover-outline--variant": css`
outline: 2px dashed #BD59FA;
`,
"visual-builder__default-cursor--disabled": css`
cursor: none;
`,
Expand Down
Loading