From e646319f5aa7e0885cb201991dfaf20b53884db5 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 21 Sep 2023 12:21:30 +1200 Subject: [PATCH] split browser sessions section into own component --- .../BrowserSessionsOverview.module.css | 27 +++++++ .../BrowserSessionsOverview.stories.tsx | 74 +++++++++++++++++++ .../BrowserSessionsOverview.test.tsx | 64 ++++++++++++++++ .../BrowserSessionsOverview.tsx | 58 +++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.module.css create mode 100644 frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.stories.tsx create mode 100644 frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.test.tsx create mode 100644 frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.tsx diff --git a/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.module.css b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.module.css new file mode 100644 index 00000000..b7a53fdc --- /dev/null +++ b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.module.css @@ -0,0 +1,27 @@ +/* Copyright 2023 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.session-list-block { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + gap: var(--cpd-space-1x); + } + +.session-list-block-info { + display: flex; + flex-direction: column; + } \ No newline at end of file diff --git a/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.stories.tsx b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.stories.tsx new file mode 100644 index 00000000..088d7d68 --- /dev/null +++ b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.stories.tsx @@ -0,0 +1,74 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { Meta, StoryObj } from "@storybook/react"; +import { Provider } from "jotai"; +import { useHydrateAtoms } from "jotai/utils"; + +import { makeFragmentData } from "../../gql"; +import { appConfigAtom, locationAtom } from "../../routing"; + +import BrowserSessionsOverview, { FRAGMENT } from "./BrowserSessionsOverview"; + +type Props = { + browserSessions: number; +}; + +const WithHomePage: React.FC> = ({ children }) => { + useHydrateAtoms([ + [appConfigAtom, { root: "/" }], + [locationAtom, { pathname: "/" }], + ]); + return <>{children}; +}; + +const Template: React.FC = ({ browserSessions }) => { + const data = makeFragmentData( + { + id: "user:123", + browserSessions: { + totalCount: browserSessions, + }, + }, + FRAGMENT, + ); + return ( + + + + + + ); +}; + +const meta = { + title: "Pages/User Sessions Overview/Browser Sessions", + component: Template, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + args: { + browserSessions: 2, + }, +}; + +export const Empty: Story = { + args: { + browserSessions: 0, + }, +}; diff --git a/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.test.tsx b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.test.tsx new file mode 100644 index 00000000..a61e17eb --- /dev/null +++ b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.test.tsx @@ -0,0 +1,64 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @vitest-environment happy-dom + +import { render, cleanup } from "@testing-library/react"; +import { describe, expect, it, afterEach } from "vitest"; + +import { makeFragmentData } from "../../gql"; +import { WithLocation } from "../../test-utils/WithLocation"; + +import BrowserSessionsOverview, { FRAGMENT } from "./BrowserSessionsOverview"; + +describe("BrowserSessionsOverview", () => { + afterEach(cleanup); + + it("renders with no browser sessions", async () => { + const user = makeFragmentData( + { + id: "user:123", + browserSessions: { + totalCount: 0, + }, + }, + FRAGMENT, + ); + const { container } = render( + + + , + ); + + expect(container).toMatchSnapshot(); + }); + + it("renders with sessions", () => { + const user = makeFragmentData( + { + id: "user:123", + browserSessions: { + totalCount: 2, + }, + }, + FRAGMENT, + ); + const { container } = render( + + + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.tsx b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.tsx new file mode 100644 index 00000000..dfd56a5f --- /dev/null +++ b/frontend/src/components/UserSessionsOverview/BrowserSessionsOverview.tsx @@ -0,0 +1,58 @@ +// Copyright 2023 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Body, H5 } from "@vector-im/compound-web"; + +import { FragmentType, graphql, useFragment } from "../../gql"; +import { Link } from "../../routing"; +import Block from "../Block"; + +import styles from "./BrowserSessionsOverview.module.css"; + +export const FRAGMENT = graphql(/* GraphQL */ ` + fragment BrowserSessionsOverview_user on User { + id + + browserSessions(first: 0, state: ACTIVE) { + totalCount + } + } +`); + +const BrowserSessionsOverview: React.FC<{ + user: FragmentType; +}> = ({ user }) => { + const data = useFragment(FRAGMENT, user); + + // allow this until we get i18n + const pluraliseSession = (count: number): string => + count === 1 ? "session" : "sessions"; + + return ( + +
+
Browsers
+ + {data.browserSessions.totalCount} active{" "} + {pluraliseSession(data.browserSessions.totalCount)} + +
+ + View all + +
+ ); +}; + +export default BrowserSessionsOverview;