add bulletin alert card UI
parent
d856a4781e
commit
ce0c161b1f
|
@ -432,7 +432,7 @@ export function fakeFarmwareInstallation(): TaggedFarmwareInstallation {
|
|||
|
||||
export function fakeAlert(): TaggedAlert {
|
||||
return fakeResource("Alert", {
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
created_at: 123,
|
||||
problem_tag: "api.noun.verb",
|
||||
priority: 100,
|
||||
|
|
|
@ -9,11 +9,13 @@ describe("API", () => {
|
|||
API.setBaseUrl(BASE);
|
||||
[
|
||||
[API.current.pointSearchPath, BASE + "/api/points/search"],
|
||||
[API.current.allPointsPath, BASE + "/api/points/?filter=all"],
|
||||
[API.current.sensorReadingPath, BASE + "/api/sensor_readings"],
|
||||
[API.current.farmwareEnvPath, BASE + "/api/farmware_envs/"],
|
||||
[API.current.plantTemplatePath, BASE + "/api/plant_templates/"],
|
||||
[API.current.diagnosticDumpsPath, BASE + "/api/diagnostic_dumps/"],
|
||||
[API.current.farmwareInstallationPath, BASE + "/api/farmware_installations/"],
|
||||
[API.current.globalBulletinPath, BASE + "/api/global_bulletins/"],
|
||||
].map(x => expect(x[0]).toEqual(x[1]));
|
||||
});
|
||||
|
||||
|
|
|
@ -154,5 +154,7 @@ export class API {
|
|||
}
|
||||
/** /api/alerts/:id */
|
||||
get alertPath() { return `${this.baseUrl}/api/alerts/`; }
|
||||
/** /api/global_bulletins/ */
|
||||
get globalBulletinPath() { return `${this.baseUrl}/api/global_bulletins/`; }
|
||||
get syncPatch() { return `${this.baseUrl}/api/device/sync/`; }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
jest.mock("axios", () => ({
|
||||
get: jest.fn(() => Promise.resolve({ data: { foo: "bar" } })),
|
||||
}));
|
||||
|
||||
jest.mock("../../api/api", () => ({
|
||||
API: { current: { globalBulletinPath: "/api/stub" } }
|
||||
}));
|
||||
|
||||
import { fetchBulletinContent } from "../actions";
|
||||
|
||||
describe("fetchBulletinContent()", () => {
|
||||
it("fetches data", async () => {
|
||||
expect(await fetchBulletinContent("slug")).toEqual({ foo: "bar" });
|
||||
});
|
||||
});
|
|
@ -9,28 +9,28 @@ const FIRMWARE_MISSING_ALERT: Alert = {
|
|||
created_at: 123,
|
||||
problem_tag: "farmbot_os.firmware.missing",
|
||||
priority: 100,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
};
|
||||
|
||||
const SEED_DATA_MISSING_ALERT: Alert = {
|
||||
created_at: 123,
|
||||
problem_tag: "api.seed_data.missing",
|
||||
priority: 300,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
};
|
||||
|
||||
const UNKNOWN_ALERT: Alert = {
|
||||
created_at: 123,
|
||||
problem_tag: "farmbot_os.firmware.alert",
|
||||
priority: 200,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
};
|
||||
|
||||
const UNKNOWN_ALERT_2: Alert = {
|
||||
created_at: 456,
|
||||
problem_tag: "farmbot_os.firmware.alert",
|
||||
priority: 100,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
};
|
||||
|
||||
describe("<Alerts />", () => {
|
||||
|
@ -84,8 +84,8 @@ describe("<FirmwareAlerts />", () => {
|
|||
it("renders alerts", () => {
|
||||
const p = fakeProps();
|
||||
p.bot.hardware.alerts = {
|
||||
"uuid1": FIRMWARE_MISSING_ALERT,
|
||||
"uuid2": UNKNOWN_ALERT
|
||||
"slug1": FIRMWARE_MISSING_ALERT,
|
||||
"slug2": UNKNOWN_ALERT
|
||||
};
|
||||
const wrapper = mount(<FirmwareAlerts {...p} />);
|
||||
expect(wrapper.text()).toContain("1");
|
||||
|
|
|
@ -2,10 +2,22 @@ jest.mock("../../devices/actions", () => ({ updateConfig: jest.fn() }));
|
|||
|
||||
jest.mock("../../api/crud", () => ({ destroy: jest.fn() }));
|
||||
|
||||
const mockData: Bulletin = {
|
||||
content: "Alert content.",
|
||||
href: "https://farm.bot",
|
||||
href_label: "See more",
|
||||
type: "info",
|
||||
slug: "slug",
|
||||
title: "Announcement",
|
||||
};
|
||||
jest.mock("../actions", () => ({
|
||||
fetchBulletinContent: jest.fn(() => Promise.resolve(mockData)),
|
||||
}));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { AlertCard, changeFirmwareHardware } from "../cards";
|
||||
import { AlertCardProps } from "../interfaces";
|
||||
import { AlertCardProps, Bulletin } from "../interfaces";
|
||||
import { fakeTimeSettings } from "../../__test_support__/fake_time_settings";
|
||||
import { FBSelect } from "../../ui";
|
||||
import { destroy } from "../../api/crud";
|
||||
|
@ -17,7 +29,7 @@ describe("<AlertCard />", () => {
|
|||
created_at: 123,
|
||||
problem_tag: "author.noun.verb",
|
||||
priority: 100,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
},
|
||||
apiFirmwareValue: undefined,
|
||||
timeSettings: fakeTimeSettings(),
|
||||
|
@ -70,6 +82,35 @@ describe("<AlertCard />", () => {
|
|||
const wrapper = mount(<AlertCard {...p} />);
|
||||
expect(wrapper.text()).toContain("Learn");
|
||||
});
|
||||
|
||||
it("renders loading bulletin card", () => {
|
||||
const p = fakeProps();
|
||||
p.alert.problem_tag = "api.bulletin.unread";
|
||||
const wrapper = mount(<AlertCard {...p} />);
|
||||
["Loading...", "Slug"].map(string =>
|
||||
expect(wrapper.text()).toContain(string));
|
||||
});
|
||||
|
||||
it("renders loaded bulletin card", async () => {
|
||||
const p = fakeProps();
|
||||
p.alert.problem_tag = "api.bulletin.unread";
|
||||
mockData.href_label = "See more";
|
||||
mockData.type = "info";
|
||||
const wrapper = await mount(<AlertCard {...p} />);
|
||||
["Loading...", "Slug"].map(string =>
|
||||
expect(wrapper.text()).not.toContain(string));
|
||||
["Announcement", "Alert content.", "See more"].map(string =>
|
||||
expect(wrapper.text()).toContain(string));
|
||||
});
|
||||
|
||||
it("renders loaded bulletin card with missing fields", async () => {
|
||||
const p = fakeProps();
|
||||
p.alert.problem_tag = "api.bulletin.unread";
|
||||
mockData.href_label = undefined;
|
||||
mockData.type = "unknown";
|
||||
const wrapper = await mount(<AlertCard {...p} />);
|
||||
expect(wrapper.text()).toContain("Find out more");
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeFirmwareHardware()", () => {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe("<Messages />", () => {
|
|||
created_at: 123,
|
||||
problem_tag: "author.noun.verb",
|
||||
priority: 100,
|
||||
uuid: "uuid",
|
||||
slug: "slug",
|
||||
}];
|
||||
const wrapper = mount(<Messages {...p} />);
|
||||
expect(wrapper.text()).toContain("Message Center");
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import axios from "axios";
|
||||
import { API } from "../api";
|
||||
import { Bulletin } from "./interfaces";
|
||||
|
||||
const url = (slug: string) => `${API.current.globalBulletinPath}/${slug}`;
|
||||
|
||||
export const fetchBulletinContent = (slug: string): Promise<Bulletin> => {
|
||||
return axios
|
||||
.get<Bulletin>(url(slug))
|
||||
.then(response => Promise.resolve(response.data));
|
||||
};
|
|
@ -4,7 +4,9 @@ import {
|
|||
AlertCardProps, AlertCardTemplateProps, FirmwareMissingProps,
|
||||
SeedDataMissingProps, SeedDataMissingState, TourNotTakenProps,
|
||||
CommonAlertCardProps,
|
||||
DismissAlertProps
|
||||
DismissAlertProps,
|
||||
Bulletin,
|
||||
BulletinAlertState
|
||||
} from "./interfaces";
|
||||
import { formatLogTime } from "../logs";
|
||||
import {
|
||||
|
@ -19,6 +21,8 @@ import {
|
|||
isFwHardwareValue
|
||||
} from "../devices/components/fbos_settings/board_type";
|
||||
import { updateConfig } from "../devices/actions";
|
||||
import { fetchBulletinContent } from "./actions";
|
||||
import { startCase } from "lodash";
|
||||
|
||||
export const AlertCard = (props: AlertCardProps) => {
|
||||
const { alert, timeSettings, findApiAlertById, dispatch } = props;
|
||||
|
@ -37,6 +41,8 @@ export const AlertCard = (props: AlertCardProps) => {
|
|||
return <UserNotWelcomed {...commonProps} />;
|
||||
case "api.documentation.unread":
|
||||
return <DocumentationUnread {...commonProps} />;
|
||||
case "api.bulletin.unread":
|
||||
return <BulletinAlert {...commonProps} />;
|
||||
default:
|
||||
return <UnknownAlert {...commonProps} />;
|
||||
}
|
||||
|
@ -62,6 +68,52 @@ const AlertCardTemplate = (props: AlertCardTemplateProps) => {
|
|||
</div>;
|
||||
};
|
||||
|
||||
const ICON_LOOKUP: { [x: string]: string } = {
|
||||
"info": "info-circle",
|
||||
"success": "check-square",
|
||||
"warn": "exclamation-triangle",
|
||||
};
|
||||
|
||||
class BulletinAlert
|
||||
extends React.Component<CommonAlertCardProps, BulletinAlertState> {
|
||||
state: BulletinAlertState = { bulletin: undefined };
|
||||
|
||||
componentDidMount() {
|
||||
fetchBulletinContent(this.props.alert.slug)
|
||||
.then(bulletin => this.setState({ bulletin }));
|
||||
}
|
||||
|
||||
get bulletinData(): Bulletin {
|
||||
return this.state.bulletin || {
|
||||
content: t("Loading..."),
|
||||
href: undefined,
|
||||
href_label: undefined,
|
||||
type: "info",
|
||||
slug: this.props.alert.slug,
|
||||
title: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { content, href, href_label, type, title } = this.bulletinData;
|
||||
return <AlertCardTemplate
|
||||
alert={this.props.alert}
|
||||
className={"bulletin-alert"}
|
||||
title={title || startCase(this.props.alert.slug)}
|
||||
iconName={ICON_LOOKUP[type] || "info"}
|
||||
message={t(content)}
|
||||
timeSettings={this.props.timeSettings}
|
||||
dispatch={this.props.dispatch}
|
||||
findApiAlertById={this.props.findApiAlertById}>
|
||||
{href && <a className="link-button fb-button green"
|
||||
href={href} target="_blank"
|
||||
title={t("Open link in a new tab")}>
|
||||
{href_label || t("Find out more")}
|
||||
</a>}
|
||||
</AlertCardTemplate>;
|
||||
}
|
||||
}
|
||||
|
||||
const UnknownAlert = (props: CommonAlertCardProps) => {
|
||||
const { problem_tag, created_at, priority } = props.alert;
|
||||
const { author, noun, verb } = splitProblemTag(problem_tag);
|
||||
|
|
|
@ -82,3 +82,16 @@ export interface SeedDataMissingState {
|
|||
export interface TourNotTakenProps extends CommonAlertCardProps {
|
||||
dispatch: Function;
|
||||
}
|
||||
|
||||
export interface Bulletin {
|
||||
content: string;
|
||||
href: string | undefined;
|
||||
href_label: string | undefined;
|
||||
type: string;
|
||||
slug: string;
|
||||
title: string | undefined;
|
||||
}
|
||||
|
||||
export interface BulletinAlertState {
|
||||
bulletin: Bulletin | undefined;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
"coveralls": "3.0.3",
|
||||
"enzyme": "3.9.0",
|
||||
"enzyme-adapter-react-16": "1.12.1",
|
||||
"farmbot": "7.0.4",
|
||||
"farmbot": "7.0.6",
|
||||
"farmbot-toastr": "1.0.3",
|
||||
"i18next": "15.0.9",
|
||||
"jest": "24.7.1",
|
||||
|
|
Loading…
Reference in New Issue