Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Publisher react app #4934

Merged
merged 24 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b7148d0
feat: Move publicise into publisher app (#4844)
steverydz Sep 9, 2024
8cacd7e
feat: Move settings into publisher app (#4847)
steverydz Sep 10, 2024
fbb281a
Wd 14634 metrics page (#4851)
ilayda-cp Sep 20, 2024
a69af94
Wd 15262 improve performance of metric page (#4863)
ilayda-cp Oct 3, 2024
f8dbf3f
chore: Move listing page into publisher app (#4865)
steverydz Oct 3, 2024
2d1670a
feat: add breadcrumb nav to the metrics page (#4871)
ilayda-cp Oct 8, 2024
175a239
feat: Create endpoints to get repo data and builds data (#4873)
steverydz Oct 8, 2024
46f36b5
feat: update badge sources (#4892)
ilayda-cp Oct 21, 2024
4453b7c
feat: Add builds section to publisher pages (#4890)
steverydz Oct 24, 2024
a7f35e7
feat: Move releases section into the publisher app (#4902)
steverydz Nov 18, 2024
a303422
fix: Fix publicise disabled notification (#4911)
steverydz Nov 20, 2024
97b72ac
fix: Fix issue with saving settings when navigating from listing (#4913)
steverydz Nov 21, 2024
47ded70
fix: Fix publicise view for private snaps (#4914)
steverydz Nov 21, 2024
655bc4c
Rebase
steverydz Nov 22, 2024
2c77db4
fix: Fix issues with tour styles (#4916)
steverydz Nov 25, 2024
e2a1395
fix: Fix code linting errors in publisher app (#4921)
steverydz Dec 4, 2024
e69125f
fix: Fix a11y errors in builds section (#4922)
steverydz Dec 5, 2024
d6aee20
fix: Fix publisher a11y errors in the publicise cards section (#4923)
steverydz Dec 5, 2024
e192b7a
fix: Fix publisher a11y errors in publicise badges section (#4924)
steverydz Dec 5, 2024
8ecf8eb
fix: Fix publisher a11y errors in publicise buttons section (#4925)
steverydz Dec 5, 2024
1d92a4b
fix: Fix publisher a11y errors in settings section (#4926)
steverydz Dec 5, 2024
ca89c9e
fix: Fix a11y errors in license search (#4928)
steverydz Dec 5, 2024
bc93343
fix: Fix a11y errors in search autocomplete (#4929)
steverydz Dec 5, 2024
74e7a1e
chore: Remove legacy publisher JS (#4930)
steverydz Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
"prettier/prettier": "error",
"react/react-in-jsx-scope": "off",
"react/no-unescaped-entities": "off",
"react/display-name": "off",
},
settings: {
react: {
Expand All @@ -43,6 +44,8 @@ module.exports = {
},
],
ignorePatterns: [
"/static/js/publisher" // skip linting any files in the publisher directory
"/static/js/publisher-pages/pages/Releases", // skip releases for now as structural changes are required to fix TS
"/static/js/publisher-pages/pages/Metrics/metrics", // skip metrics for now as structural changes are required to fix TS
"/static/js/public/snap-details/publicise.ts", // skip publicise for now as structual changes are required to fix tS
],
};
3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ module.exports = {
moduleNameMapper: {
"\\.(scss|sass|css)$": "identity-obj-proxy",
},
globals: {
fetch: global.fetch,
},
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"react-dnd-html5-backend": "16.0.1",
"react-dom": "18.2.0",
"react-hook-form": "7.53.0",
"react-joyride": "2.9.3",
"react-query": "3.39.3",
"react-redux": "8.1.3",
"react-router-dom": "6.21.1",
Expand Down
5 changes: 5 additions & 0 deletions static/js/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ declare interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Vimeo: any;
DNS_VERIFICATION_TOKEN: string;
SENTRY_DSN: string;
CSRF_TOKEN: string;
SNAP_LISTING_DATA: {
DNS_VERIFICATION_TOKEN: string;
};
}
2 changes: 1 addition & 1 deletion static/js/public/snap-details/embeddedCard.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initEmbeddedCardPicker } from "../../publisher/publicise";
import { initEmbeddedCardPicker } from "./publicise";

const showEl = (el: { classList: { remove: (arg0: string) => void } }) =>
el.classList.remove("u-hide");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function openLightbox(url: string, images: Array<any>) {
function openLightbox(url: string, images: (string | undefined)[]) {
const lightboxEl = initLightboxEl();

openLightboxEl(lightboxEl, url, images);
Expand Down Expand Up @@ -60,7 +60,7 @@ const initLightboxEl = () => {
const loadLightboxImage = (
lightboxEl: HTMLElement,
url: string | undefined,
images: Array<any>,
images: (string | undefined)[],
) => {
const contentEl = lightboxEl.querySelector(".vbox-content") as HTMLElement;

Expand Down Expand Up @@ -156,7 +156,7 @@ const loadLightboxImage = (
const openLightboxEl = (
lightboxEl: HTMLElement,
url: string,
images: Array<any>,
images: (string | undefined)[],
) => {
// prepare navigating to next/prev images
if (images && images.length) {
Expand Down
2 changes: 1 addition & 1 deletion static/js/public/snap-details/screenshots.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import lightbox from "./../../publisher/market/lightbox";
import lightbox from "./lightbox";
import Swiper from "swiper";
import { Navigation } from "swiper/modules";
import { SCREENSHOTS_CONFIG } from "../../config/swiper.config";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useLocation } from "react-router-dom";
import {
SideNavigation,
SideNavigationText,
Expand All @@ -12,6 +13,7 @@ function PrimaryNav({
collapseNavigation: boolean;
setCollapseNavigation: (value: boolean) => void;
}): JSX.Element {
const location = useLocation();
const { data: publisherData } = usePublisher();

return (
Expand Down Expand Up @@ -46,7 +48,7 @@ function PrimaryNav({
label: "My validation sets",
href: "/validation-sets",
icon: "topic",
"aria-current": "page",
"aria-current": location.pathname.includes("/validation-sets"),
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ test("the 'Revert' button is disabled by default", () => {

test("the 'Revert' button is enabled is data is dirty", () => {
renderComponent(true, false, true);
expect(screen.getByRole("button", { name: "Revert" })).not.toHaveAttribute(
"aria-disabled",
"true",
);
expect(screen.getByRole("button", { name: "Revert" })).not.toBeDisabled();
});

test("the 'Save' button is disabled by default", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useEffect, useRef } from "react";
import { useRef } from "react";
import { Row, Col, Button } from "@canonical/react-components";

import debounce from "../../../libs/debounce";

type Props = {
snapName: string | undefined;
snapName: string;
isDirty: boolean;
reset: Function;
reset: () => void;
isSaving: boolean;
isValid: boolean;
showPreview?: boolean;
Expand All @@ -20,20 +20,26 @@
showPreview,
}: Props) {
const stickyBar = useRef<HTMLDivElement>(null);
const mainPanel = document.querySelector(".l-main") as HTMLElement;

const handleScroll = () => {
stickyBar?.current?.classList.toggle(
"sticky-shadow",
stickyBar?.current?.getBoundingClientRect()?.top === 0,
);
};

useEffect(() => {
document.addEventListener("scroll", debounce(handleScroll, 10, false));
}, []);
if (mainPanel) {
mainPanel.addEventListener("scroll", debounce(handleScroll, 10, false));

Check warning on line 33 in static/js/publisher-pages/components/SaveAndPreview/SaveAndPreview.tsx

View check run for this annotation

Codecov / codecov/patch

static/js/publisher-pages/components/SaveAndPreview/SaveAndPreview.tsx#L33

Added line #L33 was not covered by tests
}

return (
<>
<div className="snapcraft-p-sticky js-sticky-bar" ref={stickyBar}>
<div
className="snapcraft-p-sticky js-sticky-bar"
ref={stickyBar}
style={{ margin: "0 -1.5rem", padding: "0 1.5rem" }}
>
<Row>
<Col size={7}>
<p className="u-no-margin--bottom">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Dispatch, SetStateAction } from "react";
import { Notification } from "@canonical/react-components";

type Props = {
hasSaved: boolean;
setHasSaved: Function;
setHasSaved: Dispatch<SetStateAction<boolean>>;
savedError: boolean | Array<{ message: string }>;
setSavedError: Function;
setSavedError: Dispatch<
SetStateAction<boolean | { code: string; message: string }[]>
>;
};

function SaveStateNotifications({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Dispatch, SetStateAction } from "react";
import { screen, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
Expand All @@ -6,9 +7,9 @@ import SaveStateNotifications from "../SaveStateNotifications";

type Options = {
hasSaved?: boolean;
setHasSaved?: Function;
setHasSaved?: Dispatch<SetStateAction<boolean>>;
savedError?: boolean | Array<{ message: string }>;
setSavedError?: Function;
setSavedError?: Dispatch<SetStateAction<boolean>>;
};

const renderComponent = (options: Options) => {
Expand All @@ -17,6 +18,7 @@ const renderComponent = (options: Options) => {
hasSaved={options.hasSaved || false}
setHasSaved={options.setHasSaved || jest.fn()}
savedError={options.savedError || false}
// @ts-expect-error Mock for testing
setSavedError={options.setSavedError || jest.fn()}
/>,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { useState, useEffect } from "react";
import Downshift from "downshift";
import { useWatch } from "react-hook-form";
import {
useWatch,
UseFormRegister,
UseFormSetValue,
UseFormGetValues,
Control,
FieldValues,
} from "react-hook-form";
import { Button } from "@canonical/react-components";

type DataItem = {
key: string;
Expand All @@ -11,10 +19,10 @@ type Props = {
data: Array<DataItem>;
field: string;
currentValues: Array<string>;
register: Function;
setValue: Function;
getValues: Function;
control: any;
register: UseFormRegister<FieldValues>;
setValue: UseFormSetValue<FieldValues>;
getValues: UseFormGetValues<FieldValues>;
control: Control<FieldValues>;
disabled?: boolean;
};

Expand Down Expand Up @@ -79,8 +87,15 @@ function SearchAutocomplete({
{selections.map((suggestion: DataItem) => (
<span key={suggestion.key} className="p-multiselect__item">
{suggestion.name}
<i
className="p-icon--close p-multiselect__item-remove"
<Button
type="button"
style={{
backgroundColor: "transparent",
border: 0,
color: "inherit",
margin: 0,
padding: 0,
}}
onClick={() => {
const newSelections = selections.filter(
(item: DataItem) => item.key !== suggestion.key,
Expand All @@ -94,8 +109,10 @@ function SearchAutocomplete({
});
}}
>
Remove suggestion
</i>
<i className="p-icon--close p-multiselect__item-remove">
Remove suggestion
</i>
</Button>
</span>
))}

Expand All @@ -119,9 +136,9 @@ function SearchAutocomplete({
)
.map((item, index) => (
<li
key={item.key}
className="p-multiselect__option"
{...getItemProps({
key: item.key,
index,
item,
style: {
Expand Down
55 changes: 55 additions & 0 deletions static/js/publisher-pages/components/SectionNav/SectionNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Link } from "react-router-dom";
import { Tabs } from "@canonical/react-components";

type Props = {
activeTab: string;
snapName: string | undefined;
};

function SectionNav({ activeTab, snapName }: Props) {
return (
<Tabs
listClassName="u-no-margin--bottom"
links={[
{
label: "Listing",
active: activeTab === "listing" || !activeTab,
to: `/${snapName}/listing`,
component: Link,
},
{
label: "Builds",
active: activeTab === "builds",
to: `/${snapName}/builds`,
component: Link,
},
{
label: "Releases",
active: activeTab === "releases",
to: `/${snapName}/releases`,
component: Link,
},
{
label: "Metrics",
active: activeTab === "metrics",
to: `/${snapName}/metrics`,
component: Link,
},
{
label: "Publicise",
active: activeTab === "publicise",
to: `/${snapName}/publicise`,
component: Link,
},
{
label: "Settings",
active: activeTab === "settings",
to: `/${snapName}/settings`,
component: Link,
},
]}
/>
);
}

export default SectionNav;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";

import PageHeader from "./PageHeader";
import SectionNav from "./SectionNav";

const snapName = "test-snap-name";

Expand All @@ -11,49 +11,49 @@ const props = {
};

test("the page displays the correct name for the snap", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
"test-snap-name",
);
});

test("the 'Listing' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Listing" }).getAttribute("href"),
).toBe(`/${snapName}/listing`);
});

test("the 'Builds' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Builds" }).getAttribute("href"),
).toBe(`/${snapName}/builds`);
});

test("the 'Releases' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Releases" }).getAttribute("href"),
).toBe(`/${snapName}/releases`);
});

test("the 'Metrics' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Metrics" }).getAttribute("href"),
).toBe(`/${snapName}/metrics`);
});

test("the 'Publicise' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Publicise" }).getAttribute("href"),
).toBe(`/${snapName}/publicise`);
});

test("the 'Settings' tab has the correct path", () => {
render(<PageHeader {...props} />);
render(<SectionNav {...props} />);
expect(
screen.getByRole("link", { name: "Settings" }).getAttribute("href"),
).toBe(`/${snapName}/settings`);
Expand Down
1 change: 1 addition & 0 deletions static/js/publisher-pages/components/SectionNav/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./SectionNav";
Loading
Loading