Skip to content
Closed
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
6 changes: 3 additions & 3 deletions apps/builder/app/builder/features/topbar/domains.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ const DomainItem = ({
<Button
formAction={handleVerify}
state={isCheckStateInProgress ? "pending" : undefined}
color="primary"
color="neutral"
css={{ width: "100%", flexShrink: 0, mt: theme.spacing[3] }}
>
Check status
Expand All @@ -364,7 +364,7 @@ const DomainItem = ({
<Button
formAction={handleUpdateStatus}
state={isCheckStateInProgress ? "pending" : undefined}
color="primary"
color="neutral"
css={{ width: "100%", flexShrink: 0, mt: theme.spacing[3] }}
>
Check status
Expand All @@ -375,7 +375,7 @@ const DomainItem = ({
<Button
formAction={handleRemoveDomain}
state={isRemoveInProgress ? "pending" : undefined}
color="neutral"
color="destructive"
css={{ width: "100%", flexShrink: 0 }}
>
Remove domain
Expand Down
38 changes: 31 additions & 7 deletions apps/builder/app/builder/features/topbar/entri.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import * as entri from "entrijs";
import { useEffect, useState } from "react";
import { useStore } from "@nanostores/react";
import { globalCss, Button, Text, toast } from "@webstudio-is/design-system";
import {
globalCss,
Button,
Text,
PanelBanner,
Flex,
Link,
} from "@webstudio-is/design-system";
import { trpcClient } from "~/shared/trpc/trpc-client";
import { $userPlanFeatures } from "~/shared/nano-states";
import { extractCname } from "./cname";
import { UploadIcon } from "@webstudio-is/icons";

// https://developers.entri.com/docs/install
type DnsRecord = {
Expand Down Expand Up @@ -96,26 +104,42 @@ export const Entri = ({ domain, dnsRecords, onClose }: EntriProps) => {
dnsRecords,
onClose,
});
const [requestUpgrade, setRequestUpgrade] = useState(false);
return (
<>
{error !== undefined && <Text color="destructive">{error}</Text>}
<Button
disabled={isOpen}
color="neutral"
css={{ width: "100%", flexShrink: 0 }}
color="primary"
type="button"
onClick={() => {
if (hasProPlan) {
showDialog();
} else {
toast.error(
"Please upgrade to the Pro plan or higher to use automatic domain configuration."
);
setRequestUpgrade(true);
}
}}
>
Configure automatically
Setup automatically with Entri
</Button>
{requestUpgrade && (
<PanelBanner>
<Text>
Please upgrade to the Pro plan or higher to use automatic domain
configuration.
</Text>
<Flex align="center" gap={1}>
<UploadIcon />
<Link
color="inherit"
target="_blank"
href="https://webstudio.is/pricing"
>
Upgrade to Pro
</Link>
</Flex>
</PanelBanner>
)}
</>
);
};
20 changes: 20 additions & 0 deletions apps/builder/app/shared/html.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,23 @@ test("generate Image component instead of img element", () => {
)
);
});

test("strip unsupported attribute names", () => {
expect(
generateFragmentFromHtml(`
<button @click="open = true">Expand</button>
<button x-on:click="open = !open">
Toggle
</button>
`)
).toEqual(
renderTemplate(
<>
<ws.element ws:tag="button">Expand</ws.element>
<ws.element ws:tag="button" x-on:click="open = !open">
Toggle
</ws.element>
</>
)
);
});
5 changes: 5 additions & 0 deletions apps/builder/app/shared/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ariaAttributes, attributesByTag } from "@webstudio-is/html-data";
import { camelCaseProperty, parseCss } from "@webstudio-is/css-data";
import { richTextContentTags } from "./content-model";
import { setIsSubsetOf } from "./shim";
import { isAttributeNameSafe } from "@webstudio-is/react-sdk";

type ElementNode = DefaultTreeAdapterMap["element"];

Expand Down Expand Up @@ -169,6 +170,10 @@ export const generateFragmentFromHtml = (
}
instances.set(instance.id, instance);
for (const attr of node.attrs) {
// skip attributes which cannot be rendered in jsx
if (!isAttributeNameSafe(attr.name)) {
continue;
}
const id = `${instance.id}:${attr.name}`;
const instanceId = instance.id;
const name = attr.name;
Expand Down
182 changes: 127 additions & 55 deletions apps/builder/app/shared/tailwind/tailwind.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ test("override border opacity", async () => {
ws:tag="div"
ws:style={css`
border-style: solid;
border-width: 1px;
border-color: rgb(229 231 235 / var(--tw-border-opacity));
border-width: 1px;
--tw-border-opacity: 0.6;
`}
></ws.element>
Expand Down Expand Up @@ -315,31 +315,6 @@ describe("extract breakpoints", () => {
);
});

test("base is first breakpoint", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element
ws:tag="div"
class="opacity-10 sm:opacity-20"
></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
@media (max-width: 479px) {
opacity: 0.1;
}
opacity: 0.2;
`}
></ws.element>
)
);
});

test("base is last breakpoint", async () => {
expect(
await generateFragmentFromTailwind(
Expand Down Expand Up @@ -393,27 +368,6 @@ describe("extract breakpoints", () => {
);
});

test("preserve breakpoint when no base breakpoint", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element ws:tag="div" class="sm:opacity-10"></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
@media (min-width: 480px) {
opacity: 0.1;
}
`}
></ws.element>
)
);
});

test("extract container class", async () => {
expect(
await generateFragmentFromTailwind(
Expand All @@ -427,7 +381,6 @@ describe("extract breakpoints", () => {
@media (max-width: 479px) {
max-width: none;
}
width: 100%;
@media (max-width: 767px) {
max-width: 640px;
}
Expand All @@ -441,6 +394,7 @@ describe("extract breakpoints", () => {
@media (min-width: 1440px) {
max-width: 1536px;
}
width: 100%;
`}
></ws.element>
)
Expand Down Expand Up @@ -501,11 +455,123 @@ describe("extract breakpoints", () => {
ws:tag="div"
ws:style={css`
opacity: 0.1;
@media (min-width: 480px) {
@media (max-width: 479px) {
&:hover {
opacity: 0.2;
opacity: unset;
}
}
&:hover {
opacity: 0.2;
}
`}
></ws.element>
)
);
});

test("adapt max-* breakpoints", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element
ws:tag="div"
class="max-sm:opacity-10 max-md:opacity-20 max-lg:opacity-30 max-xl:opacity-40 max-2xl:opacity-50 opacity-60"
></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
@media (max-width: 479px) {
opacity: 0.1;
}
@media (max-width: 767px) {
opacity: 0.2;
}
@media (max-width: 991px) {
opacity: 0.3;
}
opacity: 0.4;
@media (min-width: 1280px) {
opacity: 0.5;
}
@media (min-width: 1440px) {
opacity: 0.6;
}
`}
></ws.element>
)
);
});

test("ignore composite breakpoints", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element
ws:tag="div"
class="opacity-10 md:max-xl:flex"
></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
opacity: 0.1;
`}
></ws.element>
)
);
});

test("use unset for missing base breakpoint 1", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element ws:tag="div" class="sm:opacity-10"></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
@media (max-width: 479px) {
opacity: unset;
}
opacity: 0.1;
`}
></ws.element>
)
);
});

test("use unset for missing base breakpoint 2", async () => {
expect(
await generateFragmentFromTailwind(
renderTemplate(
<ws.element
ws:tag="div"
class="max-sm:opacity-10 md:opacity-20"
></ws.element>
)
)
).toEqual(
renderTemplate(
<ws.element
ws:tag="div"
ws:style={css`
@media (max-width: 479px) {
opacity: 0.1;
}
@media (max-width: 767px) {
opacity: unset;
}
opacity: 0.2;
`}
></ws.element>
)
Expand All @@ -518,8 +584,8 @@ test("generate space without display property", async () => {
await generateFragmentFromTailwind(
renderTemplate(
<>
<ws.element ws:tag="div" class="space-x-4"></ws.element>
<ws.element ws:tag="div" class="space-y-4"></ws.element>
<ws.element ws:tag="div" class="space-x-4 md:space-x-6"></ws.element>
<ws.element ws:tag="div" class="space-y-4 md:space-y-6"></ws.element>
</>
)
)
Expand All @@ -530,7 +596,10 @@ test("generate space without display property", async () => {
ws:tag="div"
ws:style={css`
display: flex;
column-gap: 1rem;
@media (max-width: 767px) {
column-gap: 1rem;
}
column-gap: 1.5rem;
`}
></ws.element>
<ws.element
Expand All @@ -539,7 +608,10 @@ test("generate space without display property", async () => {
display: flex;
flex-direction: column;
align-items: start;
row-gap: 1rem;
@media (max-width: 767px) {
row-gap: 1rem;
}
row-gap: 1.5rem;
`}
></ws.element>
</>
Expand Down Expand Up @@ -576,8 +648,8 @@ test("generate space with display property", async () => {
@media (max-width: 767px) {
display: none;
}
row-gap: 1rem;
display: flex;
row-gap: 1rem;
`}
></ws.element>
</>
Expand Down
Loading
Loading