Skip to content

Commit 89bca4d

Browse files
authored
feat: Publish settings section in project settings (#4295)
Closes #4272 ## Description - I moved the only publish setting we have to a separate section, so we can add more in the future. - Added a settings button in publish dialog <img width="625" alt="image" src="https://github.com/user-attachments/assets/8c72028c-69e6-4fb6-849f-bdb9ee0594cc"> <img width="327" alt="image" src="https://github.com/user-attachments/assets/70c892de-65ef-4962-a790-fb8c6e6b49e7"> ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent d29065b commit 89bca4d

File tree

9 files changed

+107
-86
lines changed

9 files changed

+107
-86
lines changed

apps/builder/app/builder/features/project-settings/project-settings.stories.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { createBrowserRouter, RouterProvider } from "react-router-dom";
2-
import { $isProjectSettingsOpen } from "~/shared/nano-states/seo";
32
import { ProjectSettingsView } from "./project-settings";
43
import { $pages } from "~/shared/nano-states";
54

65
export default {
76
component: ProjectSettingsView,
87
};
98

10-
$isProjectSettingsOpen.set(true);
119
const createRouter = (element: JSX.Element) =>
1210
createBrowserRouter([
1311
{
@@ -18,9 +16,7 @@ const createRouter = (element: JSX.Element) =>
1816
]);
1917

2018
export const General = () => {
21-
const router = createRouter(
22-
<ProjectSettingsView currentSection="General" isOpen />
23-
);
19+
const router = createRouter(<ProjectSettingsView currentSection="general" />);
2420
return <RouterProvider router={router} />;
2521
};
2622

@@ -49,7 +45,7 @@ export const Redirects = () => {
4945
});
5046

5147
const router = createRouter(
52-
<ProjectSettingsView currentSection="Redirects" isOpen />
48+
<ProjectSettingsView currentSection="redirects" />
5349
);
5450
return <RouterProvider router={router} />;
5551
};

apps/builder/app/builder/features/project-settings/project-settings.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,34 @@ import {
1111
ListItem,
1212
Text,
1313
} from "@webstudio-is/design-system";
14-
import { $isProjectSettingsOpen } from "~/shared/nano-states/seo";
14+
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
1515
import { SectionGeneral } from "./section-general";
1616
import { SectionRedirects } from "./section-redirects";
17-
import { useState } from "react";
17+
import { SectionPublish } from "./section-publish";
1818
import { SectionMarketplace } from "./section-marketplace";
1919
import { leftPanelWidth, rightPanelWidth } from "./utils";
20+
import type { FunctionComponent } from "react";
2021

21-
const sectionNames = ["General", "Redirects", "Marketplace"];
22+
type SectionName = "general" | "redirects" | "publish" | "marketplace";
2223

23-
type SectionName = (typeof sectionNames)[number];
24+
const sections = new Map<SectionName, FunctionComponent>([
25+
["general", SectionGeneral],
26+
["redirects", SectionRedirects],
27+
["publish", SectionPublish],
28+
["marketplace", SectionMarketplace],
29+
] as const);
2430

2531
export const ProjectSettingsView = ({
2632
currentSection,
2733
onSectionChange,
28-
isOpen,
2934
onOpenChange,
3035
}: {
31-
currentSection: SectionName;
36+
currentSection?: SectionName;
3237
onSectionChange?: (section: SectionName) => void;
33-
isOpen: boolean;
3438
onOpenChange?: (isOpen: boolean) => void;
3539
}) => {
3640
return (
37-
<Dialog open={isOpen} onOpenChange={onOpenChange}>
41+
<Dialog open={sections.has(currentSection!)} onOpenChange={onOpenChange}>
3842
<DialogContent
3943
css={{
4044
width: `calc(${leftPanelWidth} + ${rightPanelWidth})`,
@@ -52,7 +56,7 @@ export const ProjectSettingsView = ({
5256
borderRight: `1px solid ${theme.colors.borderMain}`,
5357
}}
5458
>
55-
{sectionNames.map((name, index) => {
59+
{Array.from(sections.keys()).map((name, index) => {
5660
return (
5761
<ListItem
5862
current={currentSection === name}
@@ -79,7 +83,7 @@ export const ProjectSettingsView = ({
7983
}}
8084
align="center"
8185
>
82-
<Text variant="labelsSentenceCase" truncate>
86+
<Text variant="labelsTitleCase" truncate>
8387
{name}
8488
</Text>
8589
</Flex>
@@ -90,9 +94,10 @@ export const ProjectSettingsView = ({
9094
</List>
9195
<ScrollArea>
9296
<Grid gap={2} css={{ my: theme.spacing[5] }}>
93-
{currentSection === "General" && <SectionGeneral />}
94-
{currentSection === "Redirects" && <SectionRedirects />}
95-
{currentSection === "Marketplace" && <SectionMarketplace />}
97+
{currentSection === "general" && <SectionGeneral />}
98+
{currentSection === "redirects" && <SectionRedirects />}
99+
{currentSection === "publish" && <SectionPublish />}
100+
{currentSection === "marketplace" && <SectionMarketplace />}
96101
<div />
97102
</Grid>
98103
</ScrollArea>
@@ -107,17 +112,15 @@ export const ProjectSettingsView = ({
107112
};
108113

109114
export const ProjectSettings = () => {
110-
const isOpen = useStore($isProjectSettingsOpen);
111-
const [currentSection, setCurrentSection] = useState<SectionName>(
112-
sectionNames[0]
113-
);
115+
const currentSection = useStore($openProjectSettings);
114116

115117
return (
116118
<ProjectSettingsView
117-
isOpen={isOpen}
118119
currentSection={currentSection}
119-
onSectionChange={setCurrentSection}
120-
onOpenChange={$isProjectSettingsOpen.set}
120+
onSectionChange={$openProjectSettings.set}
121+
onOpenChange={(open) => {
122+
$openProjectSettings.set(open ? "general" : undefined);
123+
}}
121124
/>
122125
);
123126
};

apps/builder/app/builder/features/project-settings/section-general.tsx

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
Text,
1010
Separator,
1111
Button,
12-
CheckboxAndLabel,
13-
Checkbox,
1412
css,
1513
Flex,
1614
Tooltip,
@@ -20,9 +18,8 @@ import {
2018
import { InfoCircleIcon } from "@webstudio-is/icons";
2119
import { ImageControl } from "./image-control";
2220
import { Image } from "@webstudio-is/image";
23-
import type { ProjectMeta, CompilerSettings } from "@webstudio-is/sdk";
21+
import type { ProjectMeta } from "@webstudio-is/sdk";
2422
import { $assets, $imageLoader, $pages } from "~/shared/nano-states";
25-
import { useIds } from "~/shared/form-utils";
2623
import { serverSyncStore } from "~/shared/sync";
2724
import { sectionSpacing } from "./utils";
2825
import { CodeEditor } from "~/builder/shared/code-editor";
@@ -174,52 +171,6 @@ export const SectionGeneral = () => {
174171
onChange={handleSave("code")}
175172
/>
176173
</Grid>
177-
178-
<Separator />
179-
180-
<CompilerSection />
181-
</Grid>
182-
);
183-
};
184-
185-
const defaultCompilerSettings: CompilerSettings = {
186-
atomicStyles: true,
187-
};
188-
189-
const CompilerSection = () => {
190-
const ids = useIds(["atomicStyles"]);
191-
const [settings, setSettings] = useState(
192-
() => $pages.get()?.compiler ?? defaultCompilerSettings
193-
);
194-
195-
const handleSave = (settings: CompilerSettings) => {
196-
serverSyncStore.createTransaction([$pages], (pages) => {
197-
if (pages === undefined) {
198-
return;
199-
}
200-
pages.compiler = settings;
201-
});
202-
};
203-
204-
return (
205-
<Grid gap={2} css={sectionSpacing}>
206-
<Label htmlFor={ids.atomicStyles}>Compiler</Label>
207-
<CheckboxAndLabel>
208-
<Checkbox
209-
checked={settings.atomicStyles ?? true}
210-
id={ids.atomicStyles}
211-
onCheckedChange={(atomicStyles) => {
212-
if (typeof atomicStyles === "boolean") {
213-
const nextSettings = { ...settings, atomicStyles };
214-
setSettings(nextSettings);
215-
handleSave(nextSettings);
216-
}
217-
}}
218-
/>
219-
<Label htmlFor={ids.atomicStyles}>
220-
Generate atomic CSS when publishing
221-
</Label>
222-
</CheckboxAndLabel>
223174
</Grid>
224175
);
225176
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useState } from "react";
2+
import {
3+
Grid,
4+
Label,
5+
CheckboxAndLabel,
6+
Checkbox,
7+
Text,
8+
} from "@webstudio-is/design-system";
9+
import type { CompilerSettings } from "@webstudio-is/sdk";
10+
import { $pages } from "~/shared/nano-states";
11+
import { useIds } from "~/shared/form-utils";
12+
import { serverSyncStore } from "~/shared/sync";
13+
import { sectionSpacing } from "./utils";
14+
15+
const defaultPublishSettings: CompilerSettings = {
16+
atomicStyles: true,
17+
};
18+
19+
export const SectionPublish = () => {
20+
const ids = useIds(["atomicStyles"]);
21+
const [settings, setSettings] = useState(
22+
() => $pages.get()?.compiler ?? defaultPublishSettings
23+
);
24+
25+
const handleSave = (settings: CompilerSettings) => {
26+
serverSyncStore.createTransaction([$pages], (pages) => {
27+
if (pages === undefined) {
28+
return;
29+
}
30+
pages.compiler = settings;
31+
});
32+
};
33+
34+
return (
35+
<Grid gap={2}>
36+
<Text variant="titles" css={sectionSpacing}>
37+
Publishing
38+
</Text>
39+
<Grid gap={2} css={sectionSpacing}>
40+
<CheckboxAndLabel>
41+
<Checkbox
42+
checked={settings.atomicStyles ?? true}
43+
id={ids.atomicStyles}
44+
onCheckedChange={(atomicStyles) => {
45+
if (typeof atomicStyles === "boolean") {
46+
const nextSettings = { ...settings, atomicStyles };
47+
setSettings(nextSettings);
48+
handleSave(nextSettings);
49+
}
50+
}}
51+
/>
52+
<Label htmlFor={ids.atomicStyles}>
53+
Generate atomic CSS when publishing
54+
</Label>
55+
</CheckboxAndLabel>
56+
</Grid>
57+
</Grid>
58+
);
59+
};

apps/builder/app/builder/features/sidebar-left/panels/pages/page-settings.stories.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { $pages } from "~/shared/nano-states/pages";
22
import { PageSettings } from "./page-settings";
3-
4-
import { $isProjectSettingsOpen } from "~/shared/nano-states/seo";
53
import { Grid, theme } from "@webstudio-is/design-system";
64
import { $assets, $project } from "~/shared/nano-states";
75
import { createDefaultPages } from "@webstudio-is/project-build";
@@ -17,8 +15,6 @@ export default {
1715
},
1816
};
1917

20-
$isProjectSettingsOpen.set(true);
21-
2218
$assets.set(
2319
new Map([
2420
[

apps/builder/app/builder/features/topbar/menu/menu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
} from "~/shared/nano-states";
3030
import { emitCommand } from "~/builder/shared/commands";
3131
import { MenuButton } from "./menu-button";
32-
import { $isProjectSettingsOpen } from "~/shared/nano-states/seo";
32+
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
3333
import { UpgradeIcon } from "@webstudio-is/icons";
3434
import { getSetting, setSetting } from "~/builder/shared/client-settings";
3535

@@ -95,7 +95,7 @@ export const Menu = () => {
9595
<Tooltip side="right" content={undefined}>
9696
<DropdownMenuItem
9797
onSelect={() => {
98-
$isProjectSettingsOpen.set(true);
98+
$openProjectSettings.set("general");
9999
}}
100100
>
101101
Project Settings

apps/builder/app/builder/features/topbar/publish.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
ExternalLinkIcon,
4949
AlertIcon,
5050
CopyIcon,
51+
GearIcon,
5152
} from "@webstudio-is/icons";
5253
import { AddDomain } from "./add-domain";
5354
import { humanizeString } from "~/shared/string-utils";
@@ -57,6 +58,7 @@ import type { Templates } from "@webstudio-is/sdk";
5758
import { formatDistance } from "date-fns/formatDistance";
5859
import DomainCheckbox, { domainToPublishName } from "./domain-checkbox";
5960
import { CopyToClipboard } from "~/builder/shared/copy-to-clipboard";
61+
import { $openProjectSettings } from "~/shared/nano-states/project-settings";
6062

6163
type ChangeProjectDomainProps = {
6264
project: Project;
@@ -914,7 +916,19 @@ export const PublishButton = ({ projectId }: PublishProps) => {
914916

915917
{dialogContentType === "publish" && (
916918
<>
917-
<FloatingPanelPopoverTitle>Publish</FloatingPanelPopoverTitle>
919+
<FloatingPanelPopoverTitle
920+
actions={
921+
<IconButton
922+
onClick={() => {
923+
$openProjectSettings.set("publish");
924+
}}
925+
>
926+
<GearIcon />
927+
</IconButton>
928+
}
929+
>
930+
Publish
931+
</FloatingPanelPopoverTitle>
918932
<Content projectId={projectId} onExportClick={handleExportClick} />
919933
</>
920934
)}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { atom } from "nanostores";
2+
3+
export const $openProjectSettings = atom<
4+
"general" | "redirects" | "publish" | "marketplace" | undefined
5+
>();

apps/builder/app/shared/nano-states/seo.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)