Move shared components to a packages/ directory (#30962)

* Move shared components to a packages/ directory

so they can be publish more sensibly

* Iterate towards split out shared-components module

 * Move shared component source into src/ subdir
 * Fix up imports
 * Include shared components in babel-ing (again)

* Remove now unused dependencies

* Update import in storybook preview

* ...except of course they aren't unused

if we import the shared components by source

* Ignore shared components deps

* Add shared-components to i18n paths

and upgrade web-i18n to version that supports doing so

* Move storybook stuff to shared-components

* Seems we don't need this anymore...

* Remove unused deps

and remove storybook plugin from eslint

* Presumably working-directory is only valid on run steps

* Ignore dep & run prettier

* Prettier on knips.ts

* Hopefully run in right dir

* Remember how to software write

* Okay... how about THIS way?

* Oh right, they were git ignored. Sigh.

* Add concurrently

* Ignore in knip

* Better?

* Paaaaaaaackageeeeeeees

* More packages

* Move playwright snapshots

* Still need a custom snapshots dir

* Add eslint back

* Oh, now knip sees them

* Fix another import

* Don't lint shared-components with everything else

Okay, eslint & tsconfig are tied too closely for this to work and
running tsc on the shared components will need its deps installing

* Maybe lint shared components

please?

* Not quite

* Remove storybook again

Re-check if it does work without it

* Remove storybook eslint plugin

as we're not linting storybook here anymore

* Remove this too

* We do need it here though
This commit is contained in:
David Baker
2025-10-13 10:54:50 +00:00
committed by GitHub
parent c96da5dbf8
commit 2698ad422e
177 changed files with 6179 additions and 2562 deletions
@@ -0,0 +1,31 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
.avatarWithDetails {
display: flex;
align-items: center;
border-radius: 12px;
background-color: var(--cpd-color-gray-200);
padding: var(--cpd-space-2x);
gap: var(--cpd-space-2x);
.title {
display: inline-block;
font-weight: var(--cpd-font-weight-semibold);
font-size: var(--cpd-font-size-body-md);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.details {
font-size: var(--cpd-font-size-body-sm);
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { type Meta, type StoryObj } from "@storybook/react-vite/*";
import { AvatarWithDetails } from "./AvatarWithDetails";
const meta = {
title: "Avatar/AvatarWithDetails",
component: AvatarWithDetails,
tags: ["autodocs"],
args: {
avatar: <div style={{ width: 40, height: 40, backgroundColor: "#888", borderRadius: "50%" }} />,
details: "Details about the avatar go here",
title: "Room Name",
},
} satisfies Meta<typeof AvatarWithDetails>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
@@ -0,0 +1,21 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { composeStories } from "@storybook/react-vite";
import { render } from "jest-matrix-react";
import React from "react";
import * as stories from "./AvatarWithDetails.stories.tsx";
const { Default } = composeStories(stories);
describe("AvatarWithDetails", () => {
it("renders a textual event", () => {
const { container } = render(<Default />);
expect(container).toMatchSnapshot();
});
});
@@ -0,0 +1,65 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import { type ComponentProps, type ElementType, type JSX, type PropsWithChildren } from "react";
import React from "react";
import classNames from "classnames";
import styles from "./AvatarWithDetails.module.css";
import { Flex } from "../../utils/Flex";
export type AvatarWithDetailsProps<C extends ElementType> = {
/**
* The HTML tag.
* @default "div"
*/
as?: C;
/**
* The CSS class name.
*/
className?: string;
/**
* The title/label next to the avatar. Usually the user or room name.
*/
title: string;
/**
* A label with details to display under the avatar title.
* Commonly used to display the number of participants in a room.
*/
details: React.ReactNode;
/** The avatar to display. */
avatar: React.ReactNode;
} & ComponentProps<C>;
/**
* A component to display an avatar with a title next to it in a grey box.
*
* @example
* ```tsx
* <AvatarWithDetails title="Room Name" details="10 participants" className="custom-class" />
* ```
*/
export function AvatarWithDetails<C extends React.ElementType = "div">({
as,
className,
details,
avatar,
title,
...props
}: PropsWithChildren<AvatarWithDetailsProps<C>>): JSX.Element {
const Component = as || "div";
return (
<Component className={classNames(styles.avatarWithDetails, className)} {...props}>
{avatar}
<Flex direction="column">
<span className={styles.title}>{title}</span>
<span className={styles.details}>{details}</span>
</Flex>
</Component>
);
}
@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AvatarWithDetails renders a textual event 1`] = `
<div>
<div
class="avatarWithDetails"
>
<div
style="width: 40px; height: 40px; background-color: rgb(136, 136, 136); border-radius: 50%;"
/>
<div
class="flex"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<span
class="title"
>
Room Name
</span>
<span
class="details"
>
Details about the avatar go here
</span>
</div>
</div>
</div>
`;
@@ -0,0 +1,8 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
export { AvatarWithDetails } from "./AvatarWithDetails";