Tweak default right panel size to be 320px except for maximised widgets at 420px (#110)
* Add extra buttons to room summary card Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove right panel tabs in favour of X button on each panel Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update room summary card header to align close button correctly Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix typo in pinned messages heading Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update screenshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Tweak default right panel size to be 320px except for video rooms/maximised widgets at 420px Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Track panel resizing in analytics Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix import cycle Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update screenshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update test/components/structures/MainSplit-test.tsx Co-authored-by: David Baker <dbkr@users.noreply.github.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: David Baker <dbkr@users.noreply.github.com>
|
@ -72,7 +72,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@matrix-org/analytics-events": "^0.25.0",
|
"@matrix-org/analytics-events": "^0.26.0",
|
||||||
"@matrix-org/emojibase-bindings": "^1.1.2",
|
"@matrix-org/emojibase-bindings": "^1.1.2",
|
||||||
"@matrix-org/matrix-wysiwyg": "2.37.9",
|
"@matrix-org/matrix-wysiwyg": "2.37.9",
|
||||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||||
|
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
@ -10,8 +10,10 @@ Please see LICENSE files in the repository root for full details.
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { NumberSize, Resizable } from "re-resizable";
|
import { NumberSize, Resizable } from "re-resizable";
|
||||||
import { Direction } from "re-resizable/lib/resizer";
|
import { Direction } from "re-resizable/lib/resizer";
|
||||||
|
import { WebPanelResize } from "@matrix-org/analytics-events/types/typescript/WebPanelResize";
|
||||||
|
|
||||||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||||
|
import { PosthogAnalytics } from "../../PosthogAnalytics.ts";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
resizeNotifier: ResizeNotifier;
|
resizeNotifier: ResizeNotifier;
|
||||||
|
@ -26,14 +28,16 @@ interface IProps {
|
||||||
*/
|
*/
|
||||||
sizeKey?: string;
|
sizeKey?: string;
|
||||||
/**
|
/**
|
||||||
* The size to use for the panel component if one isn't persisted in storage. Defaults to 350.
|
* The size to use for the panel component if one isn't persisted in storage. Defaults to 320.
|
||||||
*/
|
*/
|
||||||
defaultSize: number;
|
defaultSize: number;
|
||||||
|
|
||||||
|
analyticsRoomType: WebPanelResize["roomType"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MainSplit extends React.Component<IProps> {
|
export default class MainSplit extends React.Component<IProps> {
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
defaultSize: 350,
|
defaultSize: 320,
|
||||||
};
|
};
|
||||||
|
|
||||||
private onResizeStart = (): void => {
|
private onResizeStart = (): void => {
|
||||||
|
@ -58,11 +62,16 @@ export default class MainSplit extends React.Component<IProps> {
|
||||||
elementRef: HTMLElement,
|
elementRef: HTMLElement,
|
||||||
delta: NumberSize,
|
delta: NumberSize,
|
||||||
): void => {
|
): void => {
|
||||||
|
const newSize = this.loadSidePanelSize().width + delta.width;
|
||||||
this.props.resizeNotifier.stopResizing();
|
this.props.resizeNotifier.stopResizing();
|
||||||
window.localStorage.setItem(
|
window.localStorage.setItem(this.sizeSettingStorageKey, newSize.toString());
|
||||||
this.sizeSettingStorageKey,
|
|
||||||
(this.loadSidePanelSize().width + delta.width).toString(),
|
PosthogAnalytics.instance.trackEvent<WebPanelResize>({
|
||||||
);
|
eventName: "WebPanelResize",
|
||||||
|
panel: "right",
|
||||||
|
roomType: this.props.analyticsRoomType,
|
||||||
|
size: newSize,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private loadSidePanelSize(): { height: string | number; width: number } {
|
private loadSidePanelSize(): { height: string | number; width: number } {
|
||||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, createRef, ReactElement, ReactNode, RefObject, useContext } from "react";
|
import React, { ChangeEvent, ComponentProps, createRef, ReactElement, ReactNode, RefObject, useContext } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {
|
import {
|
||||||
IRecommendedVersion,
|
IRecommendedVersion,
|
||||||
|
@ -54,7 +54,7 @@ import WidgetEchoStore from "../../stores/WidgetEchoStore";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { Layout } from "../../settings/enums/Layout";
|
import { Layout } from "../../settings/enums/Layout";
|
||||||
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
||||||
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
|
import RoomContext, { TimelineRenderingType, MainSplitContentType } from "../../contexts/RoomContext";
|
||||||
import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils";
|
import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import { IMatrixClientCreds } from "../../MatrixClientPeg";
|
import { IMatrixClientCreds } from "../../MatrixClientPeg";
|
||||||
|
@ -152,13 +152,8 @@ interface IRoomProps {
|
||||||
onRegistered?(credentials: IMatrixClientCreds): void;
|
onRegistered?(credentials: IMatrixClientCreds): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This defines the content of the mainSplit.
|
export { MainSplitContentType };
|
||||||
// If the mainSplit does not contain the Timeline, the chat is shown in the right panel.
|
|
||||||
export enum MainSplitContentType {
|
|
||||||
Timeline,
|
|
||||||
MaximisedWidget,
|
|
||||||
Call,
|
|
||||||
}
|
|
||||||
export interface IRoomState {
|
export interface IRoomState {
|
||||||
room?: Room;
|
room?: Room;
|
||||||
virtualRoom?: Room;
|
virtualRoom?: Room;
|
||||||
|
@ -191,11 +186,6 @@ export interface IRoomState {
|
||||||
showApps: boolean;
|
showApps: boolean;
|
||||||
isPeeking: boolean;
|
isPeeking: boolean;
|
||||||
showRightPanel: boolean;
|
showRightPanel: boolean;
|
||||||
/**
|
|
||||||
* Whether the right panel shown is either of ThreadPanel or ThreadView.
|
|
||||||
* Always false when `showRightPanel` is false.
|
|
||||||
*/
|
|
||||||
threadRightPanel: boolean;
|
|
||||||
// error object, as from the matrix client/server API
|
// error object, as from the matrix client/server API
|
||||||
// If we failed to load information about the room,
|
// If we failed to load information about the room,
|
||||||
// store the error here.
|
// store the error here.
|
||||||
|
@ -234,7 +224,7 @@ export interface IRoomState {
|
||||||
e2eStatus?: E2EStatus;
|
e2eStatus?: E2EStatus;
|
||||||
rejecting?: boolean;
|
rejecting?: boolean;
|
||||||
hasPinnedWidgets?: boolean;
|
hasPinnedWidgets?: boolean;
|
||||||
mainSplitContentType?: MainSplitContentType;
|
mainSplitContentType: MainSplitContentType;
|
||||||
// whether or not a spaces context switch brought us here,
|
// whether or not a spaces context switch brought us here,
|
||||||
// if it did we don't want the room to be marked as read as soon as it is loaded.
|
// if it did we don't want the room to be marked as read as soon as it is loaded.
|
||||||
wasContextSwitch?: boolean;
|
wasContextSwitch?: boolean;
|
||||||
|
@ -399,7 +389,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showRightPanel: false,
|
showRightPanel: false,
|
||||||
threadRightPanel: false,
|
|
||||||
joining: false,
|
joining: false,
|
||||||
showTopUnreadMessagesBar: false,
|
showTopUnreadMessagesBar: false,
|
||||||
statusBarVisible: false,
|
statusBarVisible: false,
|
||||||
|
@ -626,11 +615,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
mainSplitContentType: room ? this.getMainSplitContentType(room) : undefined,
|
mainSplitContentType: room ? this.getMainSplitContentType(room) : undefined,
|
||||||
initialEventId: undefined, // default to clearing this, will get set later in the method if needed
|
initialEventId: undefined, // default to clearing this, will get set later in the method if needed
|
||||||
showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false,
|
showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false,
|
||||||
threadRightPanel: roomId
|
|
||||||
? [RightPanelPhases.ThreadView, RightPanelPhases.ThreadPanel].includes(
|
|
||||||
this.context.rightPanelStore.currentCardForRoom(roomId).phase!,
|
|
||||||
)
|
|
||||||
: false,
|
|
||||||
activeCall: roomId ? CallStore.instance.getActiveCall(roomId) : null,
|
activeCall: roomId ? CallStore.instance.getActiveCall(roomId) : null,
|
||||||
promptAskToJoin: this.context.roomViewStore.promptAskToJoin(),
|
promptAskToJoin: this.context.roomViewStore.promptAskToJoin(),
|
||||||
viewRoomOpts: this.context.roomViewStore.getViewRoomOpts(),
|
viewRoomOpts: this.context.roomViewStore.getViewRoomOpts(),
|
||||||
|
@ -1033,11 +1017,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
const { roomId } = this.state;
|
const { roomId } = this.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false,
|
showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false,
|
||||||
threadRightPanel: roomId
|
|
||||||
? [RightPanelPhases.ThreadView, RightPanelPhases.ThreadPanel].includes(
|
|
||||||
this.context.rightPanelStore.currentCardForRoom(roomId).phase!,
|
|
||||||
)
|
|
||||||
: false,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2531,6 +2510,17 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
const mainSplitContentClasses = classNames("mx_RoomView_body", mainSplitContentClassName);
|
const mainSplitContentClasses = classNames("mx_RoomView_body", mainSplitContentClassName);
|
||||||
|
|
||||||
|
let sizeKey: string | undefined;
|
||||||
|
let defaultSize: number | undefined;
|
||||||
|
let analyticsRoomType: ComponentProps<typeof MainSplit>["analyticsRoomType"] = "other_room";
|
||||||
|
if (this.state.mainSplitContentType !== MainSplitContentType.Timeline) {
|
||||||
|
// Override defaults for video rooms where more space is needed for the chat timeline
|
||||||
|
sizeKey = "wide";
|
||||||
|
defaultSize = 420;
|
||||||
|
analyticsRoomType =
|
||||||
|
this.state.mainSplitContentType === MainSplitContentType.Call ? "video_room" : "maximised_widget";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoomContext.Provider value={this.state}>
|
<RoomContext.Provider value={this.state}>
|
||||||
<div className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
|
<div className={mainClasses} ref={this.roomView} onKeyDown={this.onReactKeyDown}>
|
||||||
|
@ -2541,10 +2531,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
<MainSplit
|
<MainSplit
|
||||||
panel={rightPanel}
|
panel={rightPanel}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
// Override defaults when a thread is being shown to allow persisting a separate
|
sizeKey={sizeKey}
|
||||||
// right panel width for thread panels as they tend to want to be wider.
|
defaultSize={defaultSize}
|
||||||
sizeKey={this.state.threadRightPanel ? "thread" : undefined}
|
analyticsRoomType={analyticsRoomType}
|
||||||
defaultSize={this.state.threadRightPanel ? 500 : undefined}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={mainSplitContentClasses}
|
className={mainSplitContentClasses}
|
||||||
|
|
|
@ -764,7 +764,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
return (
|
return (
|
||||||
<main className="mx_SpaceRoomView">
|
<main className="mx_SpaceRoomView">
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier} analyticsRoomType="space">
|
||||||
{this.renderBody()}
|
{this.renderBody()}
|
||||||
</MainSplit>
|
</MainSplit>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
|
@ -87,7 +87,12 @@ export default class UserView extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<MainSplit panel={panel} resizeNotifier={this.props.resizeNotifier}>
|
<MainSplit
|
||||||
|
panel={panel}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
defaultSize={420}
|
||||||
|
analyticsRoomType="user_profile"
|
||||||
|
>
|
||||||
<UserOnboardingPage />
|
<UserOnboardingPage />
|
||||||
</MainSplit>
|
</MainSplit>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,14 @@ export enum TimelineRenderingType {
|
||||||
Pinned = "Pinned",
|
Pinned = "Pinned",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This defines the content of the mainSplit.
|
||||||
|
// If the mainSplit does not contain the Timeline, the chat is shown in the right panel.
|
||||||
|
export enum MainSplitContentType {
|
||||||
|
Timeline,
|
||||||
|
MaximisedWidget,
|
||||||
|
Call,
|
||||||
|
}
|
||||||
|
|
||||||
const RoomContext = createContext<
|
const RoomContext = createContext<
|
||||||
IRoomState & {
|
IRoomState & {
|
||||||
threadId?: string;
|
threadId?: string;
|
||||||
|
@ -35,7 +43,6 @@ const RoomContext = createContext<
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showRightPanel: true,
|
showRightPanel: true,
|
||||||
threadRightPanel: false,
|
|
||||||
joining: false,
|
joining: false,
|
||||||
showTopUnreadMessagesBar: false,
|
showTopUnreadMessagesBar: false,
|
||||||
statusBarVisible: false,
|
statusBarVisible: false,
|
||||||
|
@ -59,6 +66,7 @@ const RoomContext = createContext<
|
||||||
matrixClientIsReady: false,
|
matrixClientIsReady: false,
|
||||||
showUrlPreview: false,
|
showUrlPreview: false,
|
||||||
timelineRenderingType: TimelineRenderingType.Room,
|
timelineRenderingType: TimelineRenderingType.Room,
|
||||||
|
mainSplitContentType: MainSplitContentType.Timeline,
|
||||||
threadId: undefined,
|
threadId: undefined,
|
||||||
liveTimeline: undefined,
|
liveTimeline: undefined,
|
||||||
narrow: false,
|
narrow: false,
|
||||||
|
|
|
@ -7,10 +7,11 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render } from "@testing-library/react";
|
import { fireEvent, render } from "@testing-library/react";
|
||||||
|
|
||||||
import MainSplit from "../../../src/components/structures/MainSplit";
|
import MainSplit from "../../../src/components/structures/MainSplit";
|
||||||
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
||||||
|
import { PosthogAnalytics } from "../../../src/PosthogAnalytics.ts";
|
||||||
|
|
||||||
describe("<MainSplit/>", () => {
|
describe("<MainSplit/>", () => {
|
||||||
const resizeNotifier = new ResizeNotifier();
|
const resizeNotifier = new ResizeNotifier();
|
||||||
|
@ -21,18 +22,33 @@ describe("<MainSplit/>", () => {
|
||||||
);
|
);
|
||||||
const panel = <div>Right panel</div>;
|
const panel = <div>Right panel</div>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
const { asFragment, container } = render(
|
const { asFragment, container } = render(
|
||||||
<MainSplit resizeNotifier={resizeNotifier} children={children} panel={panel} />,
|
<MainSplit
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
children={children}
|
||||||
|
panel={panel}
|
||||||
|
analyticsRoomType="other_room"
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
// Assert it matches the default width of 350
|
// Assert it matches the default width of 320
|
||||||
expect(container.querySelector<HTMLElement>(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("350px");
|
expect(container.querySelector<HTMLElement>(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("320px");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("respects defaultSize prop", () => {
|
it("respects defaultSize prop", () => {
|
||||||
const { asFragment, container } = render(
|
const { asFragment, container } = render(
|
||||||
<MainSplit resizeNotifier={resizeNotifier} children={children} panel={panel} defaultSize={500} />,
|
<MainSplit
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
children={children}
|
||||||
|
panel={panel}
|
||||||
|
defaultSize={500}
|
||||||
|
analyticsRoomType="other_room"
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
// Assert it matches the default width of 350
|
// Assert it matches the default width of 350
|
||||||
|
@ -48,8 +64,36 @@ describe("<MainSplit/>", () => {
|
||||||
panel={panel}
|
panel={panel}
|
||||||
sizeKey="thread"
|
sizeKey="thread"
|
||||||
defaultSize={400}
|
defaultSize={400}
|
||||||
|
analyticsRoomType="other_room"
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(container.querySelector<HTMLElement>(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("333px");
|
expect(container.querySelector<HTMLElement>(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("333px");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should report to analytics on resize stop", () => {
|
||||||
|
const { container } = render(
|
||||||
|
<MainSplit
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
children={children}
|
||||||
|
panel={panel}
|
||||||
|
sizeKey="thread"
|
||||||
|
defaultSize={400}
|
||||||
|
analyticsRoomType="other_room"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const spy = jest.spyOn(PosthogAnalytics.instance, "trackEvent");
|
||||||
|
|
||||||
|
const handle = container.querySelector(".mx_ResizeHandle--horizontal")!;
|
||||||
|
fireEvent.mouseDown(handle);
|
||||||
|
fireEvent.mouseMove(handle, { clientX: 0 });
|
||||||
|
fireEvent.mouseUp(handle);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith({
|
||||||
|
eventName: "WebPanelResize",
|
||||||
|
panel: "right",
|
||||||
|
roomType: "other_room",
|
||||||
|
size: 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -311,6 +311,12 @@ describe("RoomView", () => {
|
||||||
expect(stores.rightPanelStore.isOpen).toEqual(true);
|
expect(stores.rightPanelStore.isOpen).toEqual(true);
|
||||||
expect(stores.rightPanelStore.currentCard.phase).toEqual(RightPanelPhases.Timeline);
|
expect(stores.rightPanelStore.currentCard.phase).toEqual(RightPanelPhases.Timeline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render joined video room view", async () => {
|
||||||
|
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||||
|
const { asFragment } = await mountRoomView();
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("for a local room", () => {
|
describe("for a local room", () => {
|
||||||
|
|
|
@ -14,7 +14,7 @@ exports[`<MainSplit/> renders 1`] = `
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_RightPanel_ResizeWrapper"
|
class="mx_RightPanel_ResizeWrapper"
|
||||||
style="position: relative; user-select: auto; width: 350px; height: 100%; max-width: 50%; min-width: 264px; box-sizing: border-box; flex-shrink: 0;"
|
style="position: relative; user-select: auto; width: 320px; height: 100%; max-width: 50%; min-width: 264px; box-sizing: border-box; flex-shrink: 0;"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
Right panel
|
Right panel
|
||||||
|
|
|
@ -1125,3 +1125,322 @@ exports[`RoomView should show error view if failed to look up room alias 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</DocumentFragment>
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView mx_RoomView_immersive"
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
aria-hidden="true"
|
||||||
|
height="768"
|
||||||
|
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||||
|
width="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="mx_MainSplit"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_body mx_MainSplit_call"
|
||||||
|
data-layout="group"
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
class="mx_Flex mx_RoomHeader light-panel"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Open room settings"
|
||||||
|
aria-live="off"
|
||||||
|
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||||
|
data-color="3"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
data-type="round"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-avatar-size: 40px;"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
class="mx_RoomHeader_infoWrapper"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Box mx_RoomHeader_info mx_Box--flex"
|
||||||
|
style="--mx-box-flex: 1;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-level="1"
|
||||||
|
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
|
||||||
|
dir="auto"
|
||||||
|
role="heading"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||||
|
>
|
||||||
|
!10:example.org
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="mx_Flex"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Chat"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Threads"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="0 members"
|
||||||
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_stacked-avatars_mcap2_111"
|
||||||
|
/>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_RightPanel_ResizeWrapper"
|
||||||
|
style="position: relative; user-select: auto; width: 420px; height: 100%; max-width: 50%; min-width: 264px; box-sizing: border-box; flex-shrink: 0;"
|
||||||
|
>
|
||||||
|
<aside
|
||||||
|
class="mx_RightPanel"
|
||||||
|
id="mx_RightPanel"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_BaseCard mx_ThreadPanel mx_TimelineCard"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_BaseCard_header"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_BaseCard_header_title"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||||
|
role="heading"
|
||||||
|
>
|
||||||
|
Chat
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||||
|
data-testid="base-card-close-button"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 28px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_TimelineCard_timeline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_messageListWrapper"
|
||||||
|
>
|
||||||
|
<ol
|
||||||
|
aria-live="polite"
|
||||||
|
class="mx_RoomView_MessageList"
|
||||||
|
style="height: 400px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-label="Message composer"
|
||||||
|
class="mx_MessageComposer mx_MessageComposer--compact"
|
||||||
|
role="region"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_wrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_SendMessageComposer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_BasicMessageComposer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Formatting"
|
||||||
|
class="mx_MessageComposerFormatBar"
|
||||||
|
role="toolbar"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Bold"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Italics"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Strikethrough"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Code block"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Quote"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Insert link"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-label="Send a message…"
|
||||||
|
aria-multiline="true"
|
||||||
|
class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty"
|
||||||
|
contenteditable="true"
|
||||||
|
data-testid="basicmessagecomposer"
|
||||||
|
dir="auto"
|
||||||
|
role="textbox"
|
||||||
|
style="--placeholder: 'Send\\ a\\ message…';"
|
||||||
|
tabindex="0"
|
||||||
|
translate="no"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_actions"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Emoji"
|
||||||
|
class="mx_AccessibleButton mx_EmojiButton mx_MessageComposer_button mx_EmojiButton_icon"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-label="Attachment"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-label="More options"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
multiple=""
|
||||||
|
style="display: none;"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_ResizeHandle--horizontal"
|
||||||
|
style="position: absolute; user-select: none; width: 10px; height: 100%; top: 0px; left: -5px; cursor: col-resize;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||||
import DocumentOffset from "../../../../src/editor/offset";
|
import DocumentOffset from "../../../../src/editor/offset";
|
||||||
import { Layout } from "../../../../src/settings/enums/Layout";
|
import { Layout } from "../../../../src/settings/enums/Layout";
|
||||||
import { IRoomState } from "../../../../src/components/structures/RoomView";
|
import { IRoomState, MainSplitContentType } from "../../../../src/components/structures/RoomView";
|
||||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||||
import { mockPlatformPeg } from "../../../test-utils/platform";
|
import { mockPlatformPeg } from "../../../test-utils/platform";
|
||||||
import { doMaybeLocalRoomAction } from "../../../../src/utils/local-room";
|
import { doMaybeLocalRoomAction } from "../../../../src/utils/local-room";
|
||||||
|
@ -47,7 +47,6 @@ describe("<SendMessageComposer/>", () => {
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showRightPanel: true,
|
showRightPanel: true,
|
||||||
threadRightPanel: false,
|
|
||||||
joining: false,
|
joining: false,
|
||||||
atEndOfLiveTimeline: true,
|
atEndOfLiveTimeline: true,
|
||||||
showTopUnreadMessagesBar: false,
|
showTopUnreadMessagesBar: false,
|
||||||
|
@ -69,6 +68,7 @@ describe("<SendMessageComposer/>", () => {
|
||||||
showDisplaynameChanges: true,
|
showDisplaynameChanges: true,
|
||||||
matrixClientIsReady: false,
|
matrixClientIsReady: false,
|
||||||
timelineRenderingType: TimelineRenderingType.Room,
|
timelineRenderingType: TimelineRenderingType.Room,
|
||||||
|
mainSplitContentType: MainSplitContentType.Timeline,
|
||||||
liveTimeline: undefined,
|
liveTimeline: undefined,
|
||||||
canSelfRedact: false,
|
canSelfRedact: false,
|
||||||
resizing: false,
|
resizing: false,
|
||||||
|
|
|
@ -7,10 +7,10 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MockedObject } from "jest-mock";
|
import { MockedObject } from "jest-mock";
|
||||||
import { MatrixClient, MatrixEvent, EventType, Room, EventTimeline } from "matrix-js-sdk/src/matrix";
|
import { EventTimeline, EventType, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import { IRoomState } from "../../src/components/structures/RoomView";
|
import { IRoomState, MainSplitContentType } from "../../src/components/structures/RoomView";
|
||||||
import { TimelineRenderingType } from "../../src/contexts/RoomContext";
|
import { TimelineRenderingType } from "../../src/contexts/RoomContext";
|
||||||
import { Layout } from "../../src/settings/enums/Layout";
|
import { Layout } from "../../src/settings/enums/Layout";
|
||||||
import { mkEvent } from "./test-utils";
|
import { mkEvent } from "./test-utils";
|
||||||
|
@ -54,7 +54,6 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
|
||||||
showApps: false,
|
showApps: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
showRightPanel: true,
|
showRightPanel: true,
|
||||||
threadRightPanel: false,
|
|
||||||
joining: false,
|
joining: false,
|
||||||
atEndOfLiveTimeline: true,
|
atEndOfLiveTimeline: true,
|
||||||
showTopUnreadMessagesBar: false,
|
showTopUnreadMessagesBar: false,
|
||||||
|
@ -76,6 +75,7 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
|
||||||
showDisplaynameChanges: true,
|
showDisplaynameChanges: true,
|
||||||
matrixClientIsReady: false,
|
matrixClientIsReady: false,
|
||||||
timelineRenderingType: TimelineRenderingType.Room,
|
timelineRenderingType: TimelineRenderingType.Room,
|
||||||
|
mainSplitContentType: MainSplitContentType.Timeline,
|
||||||
liveTimeline: undefined,
|
liveTimeline: undefined,
|
||||||
canSelfRedact: false,
|
canSelfRedact: false,
|
||||||
resizing: false,
|
resizing: false,
|
||||||
|
|
|
@ -1893,10 +1893,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
|
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
|
||||||
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
||||||
|
|
||||||
"@matrix-org/analytics-events@^0.25.0":
|
"@matrix-org/analytics-events@^0.26.0":
|
||||||
version "0.25.0"
|
version "0.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.25.0.tgz#b0b85297dc05a67feaf89cc5d70b80283c988141"
|
resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.26.0.tgz#7c8f8f924d8313c87951a0e941640ef8ff78f3d6"
|
||||||
integrity sha512-UCTuMjlJGArMqG9qXGfeNz/XtZDFldwuO+dkqP6Wo1nVdWasoWAOlcimDWQ2JnNFCg+UDZU+HLBdS5juTd6xTg==
|
integrity sha512-cjKZBejajUG8wPhVygMkBTwTLdEn74luUP6g6RjCUqPR3RYIl3NVi58Zil8CWfRTILb4wVLCPpAvehgXJn1HnQ==
|
||||||
|
|
||||||
"@matrix-org/emojibase-bindings@^1.1.2":
|
"@matrix-org/emojibase-bindings@^1.1.2":
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
|
@ -9599,7 +9599,6 @@ which@^2.0.1:
|
||||||
isexe "^2.0.0"
|
isexe "^2.0.0"
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||||
name wrap-ansi-cjs
|
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
|
|