mirror of
https://github.com/element-hq/element-web
synced 2024-11-23 17:56:01 +03:00
Allow editing polls (#7806)
This commit is contained in:
parent
fa9af44523
commit
7387f3c80a
8 changed files with 310 additions and 38 deletions
|
@ -16,7 +16,8 @@ limitations under the License.
|
||||||
|
|
||||||
import React, { ChangeEvent, createRef } from "react";
|
import React, { ChangeEvent, createRef } from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { M_POLL_KIND_DISCLOSED, PollStartEvent } from "matrix-events-sdk";
|
import { IPartialEvent, M_POLL_KIND_DISCLOSED, M_POLL_START, PollStartEvent } from "matrix-events-sdk";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import ScrollableBaseModal, { IScrollableBaseState } from "../dialogs/ScrollableBaseModal";
|
import ScrollableBaseModal, { IScrollableBaseState } from "../dialogs/ScrollableBaseModal";
|
||||||
import { IDialogProps } from "../dialogs/IDialogProps";
|
import { IDialogProps } from "../dialogs/IDialogProps";
|
||||||
|
@ -31,6 +32,7 @@ import Spinner from "./Spinner";
|
||||||
interface IProps extends IDialogProps {
|
interface IProps extends IDialogProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
threadId?: string;
|
threadId?: string;
|
||||||
|
editingMxEvent?: MatrixEvent; // Truthy if we are editing an existing poll
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState extends IScrollableBaseState {
|
interface IState extends IScrollableBaseState {
|
||||||
|
@ -45,21 +47,42 @@ const DEFAULT_NUM_OPTIONS = 2;
|
||||||
const MAX_QUESTION_LENGTH = 340;
|
const MAX_QUESTION_LENGTH = 340;
|
||||||
const MAX_OPTION_LENGTH = 340;
|
const MAX_OPTION_LENGTH = 340;
|
||||||
|
|
||||||
|
function creatingInitialState(): IState {
|
||||||
|
return {
|
||||||
|
title: _t("Create poll"),
|
||||||
|
actionLabel: _t("Create Poll"),
|
||||||
|
canSubmit: false, // need to add a question and at least one option first
|
||||||
|
question: "",
|
||||||
|
options: arraySeed("", DEFAULT_NUM_OPTIONS),
|
||||||
|
busy: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function editingInitialState(editingMxEvent: MatrixEvent): IState {
|
||||||
|
const poll = editingMxEvent.unstableExtensibleEvent as PollStartEvent;
|
||||||
|
if (!poll?.isEquivalentTo(M_POLL_START)) return creatingInitialState();
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: _t("Edit poll"),
|
||||||
|
actionLabel: _t("Done"),
|
||||||
|
canSubmit: true,
|
||||||
|
question: poll.question.text,
|
||||||
|
options: poll.answers.map(ans => ans.text),
|
||||||
|
busy: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState> {
|
export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState> {
|
||||||
private addOptionRef = createRef<HTMLDivElement>();
|
private addOptionRef = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = (
|
||||||
title: _t("Create poll"),
|
props.editingMxEvent
|
||||||
actionLabel: _t("Create Poll"),
|
? editingInitialState(props.editingMxEvent)
|
||||||
canSubmit: false, // need to add a question and at least one option first
|
: creatingInitialState()
|
||||||
|
);
|
||||||
question: "",
|
|
||||||
options: arraySeed("", DEFAULT_NUM_OPTIONS),
|
|
||||||
busy: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkCanSubmit() {
|
private checkCanSubmit() {
|
||||||
|
@ -97,13 +120,32 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
protected submit(): void {
|
private createEvent(): IPartialEvent<object> {
|
||||||
this.setState({ busy: true, canSubmit: false });
|
const pollStart = PollStartEvent.from(
|
||||||
const pollEvent = PollStartEvent.from(
|
|
||||||
this.state.question.trim(),
|
this.state.question.trim(),
|
||||||
this.state.options.map(a => a.trim()).filter(a => !!a),
|
this.state.options.map(a => a.trim()).filter(a => !!a),
|
||||||
M_POLL_KIND_DISCLOSED,
|
M_POLL_KIND_DISCLOSED,
|
||||||
).serialize();
|
).serialize();
|
||||||
|
|
||||||
|
if (!this.props.editingMxEvent) {
|
||||||
|
return pollStart;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
"content": {
|
||||||
|
"m.new_content": pollStart.content,
|
||||||
|
"m.relates_to": {
|
||||||
|
"rel_type": "m.replace",
|
||||||
|
"event_id": this.props.editingMxEvent.getId(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": pollStart.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected submit(): void {
|
||||||
|
this.setState({ busy: true, canSubmit: false });
|
||||||
|
const pollEvent = this.createEvent();
|
||||||
this.matrixClient.sendEvent(
|
this.matrixClient.sendEvent(
|
||||||
this.props.room.roomId,
|
this.props.room.roomId,
|
||||||
this.props.threadId,
|
this.props.threadId,
|
||||||
|
@ -159,7 +201,10 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
|
||||||
maxLength={MAX_OPTION_LENGTH}
|
maxLength={MAX_OPTION_LENGTH}
|
||||||
label={_t("Option %(number)s", { number: i + 1 })}
|
label={_t("Option %(number)s", { number: i + 1 })}
|
||||||
placeholder={_t("Write an option")}
|
placeholder={_t("Write an option")}
|
||||||
onChange={e => this.onOptionChange(i, e)}
|
onChange={
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
this.onOptionChange(i, e)
|
||||||
|
}
|
||||||
usePlaceholderAsHint={true}
|
usePlaceholderAsHint={true}
|
||||||
disabled={this.state.busy}
|
disabled={this.state.busy}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -45,6 +45,28 @@ interface IState {
|
||||||
endRelations: RelatedRelations; // Poll end events
|
endRelations: RelatedRelations; // Poll end events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createVoteRelations(
|
||||||
|
getRelationsForEvent: (
|
||||||
|
eventId: string,
|
||||||
|
relationType: string,
|
||||||
|
eventType: string
|
||||||
|
) => Relations,
|
||||||
|
eventId: string,
|
||||||
|
) {
|
||||||
|
return new RelatedRelations([
|
||||||
|
getRelationsForEvent(
|
||||||
|
eventId,
|
||||||
|
"m.reference",
|
||||||
|
M_POLL_RESPONSE.name,
|
||||||
|
),
|
||||||
|
getRelationsForEvent(
|
||||||
|
eventId,
|
||||||
|
"m.reference",
|
||||||
|
M_POLL_RESPONSE.altName,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
export function findTopAnswer(
|
export function findTopAnswer(
|
||||||
pollEvent: MatrixEvent,
|
pollEvent: MatrixEvent,
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
|
@ -68,18 +90,7 @@ export function findTopAnswer(
|
||||||
return poll.answers.find(a => a.id === answerId)?.text ?? "";
|
return poll.answers.find(a => a.id === answerId)?.text ?? "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const voteRelations = new RelatedRelations([
|
const voteRelations = createVoteRelations(getRelationsForEvent, pollEvent.getId());
|
||||||
getRelationsForEvent(
|
|
||||||
pollEvent.getId(),
|
|
||||||
"m.reference",
|
|
||||||
M_POLL_RESPONSE.name,
|
|
||||||
),
|
|
||||||
getRelationsForEvent(
|
|
||||||
pollEvent.getId(),
|
|
||||||
"m.reference",
|
|
||||||
M_POLL_RESPONSE.altName,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const endRelations = new RelatedRelations([
|
const endRelations = new RelatedRelations([
|
||||||
getRelationsForEvent(
|
getRelationsForEvent(
|
||||||
|
|
|
@ -20,6 +20,7 @@ import React, { ReactElement, useEffect } from 'react';
|
||||||
import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { MsgType } from 'matrix-js-sdk/src/@types/event';
|
import { MsgType } from 'matrix-js-sdk/src/@types/event';
|
||||||
|
import { M_POLL_START } from 'matrix-events-sdk';
|
||||||
|
|
||||||
import type { Relations } from 'matrix-js-sdk/src/models/relations';
|
import type { Relations } from 'matrix-js-sdk/src/models/relations';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -42,6 +43,10 @@ import ReplyChain from '../elements/ReplyChain';
|
||||||
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
|
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
|
||||||
import ReactionPicker from "../emojipicker/ReactionPicker";
|
import ReactionPicker from "../emojipicker/ReactionPicker";
|
||||||
import { CardContext } from '../right_panel/BaseCard';
|
import { CardContext } from '../right_panel/BaseCard';
|
||||||
|
import Modal from '../../../Modal';
|
||||||
|
import PollCreateDialog from '../elements/PollCreateDialog';
|
||||||
|
import ErrorDialog from '../dialogs/ErrorDialog';
|
||||||
|
import { createVoteRelations } from './MPollBody';
|
||||||
|
|
||||||
interface IOptionsButtonProps {
|
interface IOptionsButtonProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -228,12 +233,59 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEditClick = (ev: React.MouseEvent): void => {
|
private pollAlreadyHasVotes = (): boolean => {
|
||||||
dis.dispatch({
|
if (!this.props.getRelationsForEvent) {
|
||||||
action: Action.EditEvent,
|
return false;
|
||||||
event: this.props.mxEvent,
|
}
|
||||||
timelineRenderingType: this.context.timelineRenderingType,
|
|
||||||
});
|
const voteRelations = createVoteRelations(
|
||||||
|
this.props.getRelationsForEvent,
|
||||||
|
this.props.mxEvent.getId(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return voteRelations.getRelations().length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private launchPollEditor = (): void => {
|
||||||
|
if (this.pollAlreadyHasVotes()) {
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
'Not allowed to edit poll',
|
||||||
|
'',
|
||||||
|
ErrorDialog,
|
||||||
|
{
|
||||||
|
title: _t("Can't edit poll"),
|
||||||
|
description: _t(
|
||||||
|
"Sorry, you can't edit a poll after votes have been cast.",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
'Polls',
|
||||||
|
'create',
|
||||||
|
PollCreateDialog,
|
||||||
|
{
|
||||||
|
room: this.context.room,
|
||||||
|
threadId: this.context.threadId ?? null,
|
||||||
|
editingMxEvent: this.props.mxEvent,
|
||||||
|
},
|
||||||
|
'mx_CompoundDialog',
|
||||||
|
false, // isPriorityModal
|
||||||
|
true, // isStaticModal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private onEditClick = (): void => {
|
||||||
|
if (M_POLL_START.matches(this.props.mxEvent.getType())) {
|
||||||
|
this.launchPollEditor();
|
||||||
|
} else {
|
||||||
|
dis.dispatch({
|
||||||
|
action: Action.EditEvent,
|
||||||
|
event: this.props.mxEvent,
|
||||||
|
timelineRenderingType: this.context.timelineRenderingType,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly forbiddenThreadHeadMsgType = [
|
private readonly forbiddenThreadHeadMsgType = [
|
||||||
|
|
|
@ -2092,6 +2092,8 @@
|
||||||
"Go": "Go",
|
"Go": "Go",
|
||||||
"Error processing audio message": "Error processing audio message",
|
"Error processing audio message": "Error processing audio message",
|
||||||
"React": "React",
|
"React": "React",
|
||||||
|
"Can't edit poll": "Can't edit poll",
|
||||||
|
"Sorry, you can't edit a poll after votes have been cast.": "Sorry, you can't edit a poll after votes have been cast.",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
"Reply in thread": "Reply in thread",
|
"Reply in thread": "Reply in thread",
|
||||||
"Reply": "Reply",
|
"Reply": "Reply",
|
||||||
|
@ -2314,6 +2316,8 @@
|
||||||
"Language Dropdown": "Language Dropdown",
|
"Language Dropdown": "Language Dropdown",
|
||||||
"Create poll": "Create poll",
|
"Create poll": "Create poll",
|
||||||
"Create Poll": "Create Poll",
|
"Create Poll": "Create Poll",
|
||||||
|
"Edit poll": "Edit poll",
|
||||||
|
"Done": "Done",
|
||||||
"Failed to post poll": "Failed to post poll",
|
"Failed to post poll": "Failed to post poll",
|
||||||
"Sorry, the poll you tried to create was not posted.": "Sorry, the poll you tried to create was not posted.",
|
"Sorry, the poll you tried to create was not posted.": "Sorry, the poll you tried to create was not posted.",
|
||||||
"What is your poll question or topic?": "What is your poll question or topic?",
|
"What is your poll question or topic?": "What is your poll question or topic?",
|
||||||
|
@ -2817,7 +2821,6 @@
|
||||||
"Not Trusted": "Not Trusted",
|
"Not Trusted": "Not Trusted",
|
||||||
"Manually Verify by Text": "Manually Verify by Text",
|
"Manually Verify by Text": "Manually Verify by Text",
|
||||||
"Interactively verify by Emoji": "Interactively verify by Emoji",
|
"Interactively verify by Emoji": "Interactively verify by Emoji",
|
||||||
"Done": "Done",
|
|
||||||
"Upload files (%(current)s of %(total)s)": "Upload files (%(current)s of %(total)s)",
|
"Upload files (%(current)s of %(total)s)": "Upload files (%(current)s of %(total)s)",
|
||||||
"Upload files": "Upload files",
|
"Upload files": "Upload files",
|
||||||
"Upload all": "Upload all",
|
"Upload all": "Upload all",
|
||||||
|
|
|
@ -58,8 +58,14 @@ export function isContentActionable(mxEvent: MatrixEvent): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canEditContent(mxEvent: MatrixEvent): boolean {
|
export function canEditContent(mxEvent: MatrixEvent): boolean {
|
||||||
if (mxEvent.status === EventStatus.CANCELLED ||
|
const isCancellable = (
|
||||||
mxEvent.getType() !== EventType.RoomMessage ||
|
mxEvent.getType() === EventType.RoomMessage ||
|
||||||
|
M_POLL_START.matches(mxEvent.getType())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isCancellable ||
|
||||||
|
mxEvent.status === EventStatus.CANCELLED ||
|
||||||
mxEvent.isRedacted() ||
|
mxEvent.isRedacted() ||
|
||||||
mxEvent.isRelation(RelationType.Replace) ||
|
mxEvent.isRelation(RelationType.Replace) ||
|
||||||
mxEvent.getSender() !== MatrixClientPeg.get().getUserId()
|
mxEvent.getSender() !== MatrixClientPeg.get().getUserId()
|
||||||
|
@ -68,7 +74,14 @@ export function canEditContent(mxEvent: MatrixEvent): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { msgtype, body } = mxEvent.getOriginalContent();
|
const { msgtype, body } = mxEvent.getOriginalContent();
|
||||||
return (msgtype === MsgType.Text || msgtype === MsgType.Emote) && body && typeof body === 'string';
|
return (
|
||||||
|
M_POLL_START.matches(mxEvent.getType()) ||
|
||||||
|
(
|
||||||
|
(msgtype === MsgType.Text || msgtype === MsgType.Emote) &&
|
||||||
|
body &&
|
||||||
|
typeof body === 'string'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canEditOwnEvent(mxEvent: MatrixEvent): boolean {
|
export function canEditOwnEvent(mxEvent: MatrixEvent): boolean {
|
||||||
|
|
|
@ -19,6 +19,8 @@ import '../../../skinned-sdk';
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { mount, ReactWrapper } from "enzyme";
|
import { mount, ReactWrapper } from "enzyme";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { M_POLL_KIND_DISCLOSED, M_POLL_START, M_TEXT, PollStartEvent } from 'matrix-events-sdk';
|
||||||
|
import { IContent, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
|
|
||||||
import * as TestUtils from "../../../test-utils";
|
import * as TestUtils from "../../../test-utils";
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
|
@ -64,6 +66,27 @@ describe("PollCreateDialog", () => {
|
||||||
expect(dialog.html()).toMatchSnapshot();
|
expect(dialog.html()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders info from a previous event", () => {
|
||||||
|
const previousEvent: MatrixEvent = new MatrixEvent(
|
||||||
|
PollStartEvent.from(
|
||||||
|
"Poll Q",
|
||||||
|
["Answer 1", "Answer 2"],
|
||||||
|
M_POLL_KIND_DISCLOSED,
|
||||||
|
).serialize(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const dialog = mount(
|
||||||
|
<PollCreateDialog
|
||||||
|
room={createRoom()}
|
||||||
|
onFinished={jest.fn()}
|
||||||
|
editingMxEvent={previousEvent}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(submitIsDisabled(dialog)).toBe(false);
|
||||||
|
expect(dialog.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
it("doesn't allow submitting until there are options", () => {
|
it("doesn't allow submitting until there are options", () => {
|
||||||
const dialog = mount(
|
const dialog = mount(
|
||||||
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||||
|
@ -102,6 +125,129 @@ describe("PollCreateDialog", () => {
|
||||||
dialog.find("button").simulate("click");
|
dialog.find("button").simulate("click");
|
||||||
expect(dialog.find("Spinner").length).toBe(1);
|
expect(dialog.find("Spinner").length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sends a poll create event when submitted", () => {
|
||||||
|
TestUtils.stubClient();
|
||||||
|
let sentEventContent: IContent = null;
|
||||||
|
MatrixClientPeg.get().sendEvent = jest.fn(
|
||||||
|
(
|
||||||
|
_roomId: string,
|
||||||
|
_threadId: string,
|
||||||
|
eventType: string,
|
||||||
|
content: IContent,
|
||||||
|
) => {
|
||||||
|
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
||||||
|
sentEventContent = content;
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const dialog = mount(
|
||||||
|
<PollCreateDialog room={createRoom()} onFinished={jest.fn()} />,
|
||||||
|
);
|
||||||
|
changeValue(dialog, "Question or topic", "Q");
|
||||||
|
changeValue(dialog, "Option 1", "A1");
|
||||||
|
changeValue(dialog, "Option 2", "A2");
|
||||||
|
|
||||||
|
dialog.find("button").simulate("click");
|
||||||
|
expect(sentEventContent).toEqual(
|
||||||
|
{
|
||||||
|
[M_TEXT.name]: "Q\n1. A1\n2. A2",
|
||||||
|
[M_POLL_START.name]: {
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"id": expect.any(String),
|
||||||
|
[M_TEXT.name]: "A1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": expect.any(String),
|
||||||
|
[M_TEXT.name]: "A2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"kind": M_POLL_KIND_DISCLOSED.name,
|
||||||
|
"max_selections": 1,
|
||||||
|
"question": {
|
||||||
|
"body": "Q",
|
||||||
|
"format": undefined,
|
||||||
|
"formatted_body": undefined,
|
||||||
|
"msgtype": "m.text",
|
||||||
|
[M_TEXT.name]: "Q",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sends a poll edit event when editing", () => {
|
||||||
|
TestUtils.stubClient();
|
||||||
|
let sentEventContent: IContent = null;
|
||||||
|
MatrixClientPeg.get().sendEvent = jest.fn(
|
||||||
|
(
|
||||||
|
_roomId: string,
|
||||||
|
_threadId: string,
|
||||||
|
eventType: string,
|
||||||
|
content: IContent,
|
||||||
|
) => {
|
||||||
|
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
||||||
|
sentEventContent = content;
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const previousEvent: MatrixEvent = new MatrixEvent(
|
||||||
|
PollStartEvent.from(
|
||||||
|
"Poll Q",
|
||||||
|
["Answer 1", "Answer 2"],
|
||||||
|
M_POLL_KIND_DISCLOSED,
|
||||||
|
).serialize(),
|
||||||
|
);
|
||||||
|
previousEvent.event.event_id = "$prevEventId";
|
||||||
|
|
||||||
|
const dialog = mount(
|
||||||
|
<PollCreateDialog
|
||||||
|
room={createRoom()}
|
||||||
|
onFinished={jest.fn()}
|
||||||
|
editingMxEvent={previousEvent}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
changeValue(dialog, "Question or topic", "Poll Q updated");
|
||||||
|
changeValue(dialog, "Option 2", "Answer 2 updated");
|
||||||
|
dialog.find("button").simulate("click");
|
||||||
|
|
||||||
|
expect(sentEventContent).toEqual(
|
||||||
|
{
|
||||||
|
"m.new_content": {
|
||||||
|
[M_TEXT.name]: "Poll Q updated\n1. Answer 1\n2. Answer 2 updated",
|
||||||
|
[M_POLL_START.name]: {
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"id": expect.any(String),
|
||||||
|
[M_TEXT.name]: "Answer 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": expect.any(String),
|
||||||
|
[M_TEXT.name]: "Answer 2 updated",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"kind": M_POLL_KIND_DISCLOSED.name,
|
||||||
|
"max_selections": 1,
|
||||||
|
"question": {
|
||||||
|
"body": "Poll Q updated",
|
||||||
|
"format": undefined,
|
||||||
|
"formatted_body": undefined,
|
||||||
|
"msgtype": "m.text",
|
||||||
|
[M_TEXT.name]: "Poll Q updated",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"m.relates_to": {
|
||||||
|
"event_id": previousEvent.getId(),
|
||||||
|
"rel_type": "m.replace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function createRoom(): Room {
|
function createRoom(): Room {
|
||||||
|
|
|
@ -3,3 +3,5 @@
|
||||||
exports[`PollCreateDialog renders a blank poll 1`] = `"<div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\" role=\\"dialog\\" aria-labelledby=\\"mx_CompoundDialog_title\\" aria-describedby=\\"mx_CompoundDialog_content\\" class=\\"mx_CompoundDialog mx_ScrollableBaseDialog\\"><div class=\\"mx_CompoundDialog_header\\"><h1>Create poll</h1><div aria-label=\\"Close dialog\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_CompoundDialog_cancelButton\\"></div></div><form><div class=\\"mx_CompoundDialog_content\\"><div class=\\"mx_PollCreateDialog\\"><h2>What is your poll question or topic?</h2><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Question or topic\\" placeholder=\\"Write something...\\" type=\\"text\\" id=\\"mx_Field_1\\" value=\\"\\"><label for=\\"mx_Field_1\\">Question or topic</label></div><h2>Create options</h2><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 1\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_2\\" value=\\"\\"><label for=\\"mx_Field_2\\">Option 1</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 2\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_3\\" value=\\"\\"><label for=\\"mx_Field_3\\">Option 2</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_addOption mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary\\">Add option</div></div></div><div class=\\"mx_CompoundDialog_footer\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline\\">Cancel</div><button type=\\"submit\\" role=\\"button\\" tabindex=\\"0\\" aria-disabled=\\"true\\" class=\\"mx_AccessibleButton mx_Dialog_nonDialogButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary mx_AccessibleButton_disabled\\">Create Poll</button></div></form></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div>"`;
|
exports[`PollCreateDialog renders a blank poll 1`] = `"<div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\" role=\\"dialog\\" aria-labelledby=\\"mx_CompoundDialog_title\\" aria-describedby=\\"mx_CompoundDialog_content\\" class=\\"mx_CompoundDialog mx_ScrollableBaseDialog\\"><div class=\\"mx_CompoundDialog_header\\"><h1>Create poll</h1><div aria-label=\\"Close dialog\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_CompoundDialog_cancelButton\\"></div></div><form><div class=\\"mx_CompoundDialog_content\\"><div class=\\"mx_PollCreateDialog\\"><h2>What is your poll question or topic?</h2><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Question or topic\\" placeholder=\\"Write something...\\" type=\\"text\\" id=\\"mx_Field_1\\" value=\\"\\"><label for=\\"mx_Field_1\\">Question or topic</label></div><h2>Create options</h2><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 1\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_2\\" value=\\"\\"><label for=\\"mx_Field_2\\">Option 1</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 2\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_3\\" value=\\"\\"><label for=\\"mx_Field_3\\">Option 2</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_addOption mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary\\">Add option</div></div></div><div class=\\"mx_CompoundDialog_footer\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline\\">Cancel</div><button type=\\"submit\\" role=\\"button\\" tabindex=\\"0\\" aria-disabled=\\"true\\" class=\\"mx_AccessibleButton mx_Dialog_nonDialogButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary mx_AccessibleButton_disabled\\">Create Poll</button></div></form></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div>"`;
|
||||||
|
|
||||||
exports[`PollCreateDialog renders a question and some options 1`] = `"<div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\" role=\\"dialog\\" aria-labelledby=\\"mx_CompoundDialog_title\\" aria-describedby=\\"mx_CompoundDialog_content\\" class=\\"mx_CompoundDialog mx_ScrollableBaseDialog\\"><div class=\\"mx_CompoundDialog_header\\"><h1>Create poll</h1><div aria-label=\\"Close dialog\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_CompoundDialog_cancelButton\\"></div></div><form><div class=\\"mx_CompoundDialog_content\\"><div class=\\"mx_PollCreateDialog\\"><h2>What is your poll question or topic?</h2><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Question or topic\\" placeholder=\\"Write something...\\" type=\\"text\\" id=\\"mx_Field_4\\" value=\\"How many turnips is the optimal number?\\"><label for=\\"mx_Field_4\\">Question or topic</label></div><h2>Create options</h2><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 1\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_5\\" value=\\"As many as my neighbour\\"><label for=\\"mx_Field_5\\">Option 1</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 2\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_6\\" value=\\"The question is meaningless\\"><label for=\\"mx_Field_6\\">Option 2</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 3\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_7\\" value=\\"Mu\\"><label for=\\"mx_Field_7\\">Option 3</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_addOption mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary\\">Add option</div></div></div><div class=\\"mx_CompoundDialog_footer\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline\\">Cancel</div><button type=\\"submit\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_Dialog_nonDialogButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary\\">Create Poll</button></div></form></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div>"`;
|
exports[`PollCreateDialog renders a question and some options 1`] = `"<div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\" role=\\"dialog\\" aria-labelledby=\\"mx_CompoundDialog_title\\" aria-describedby=\\"mx_CompoundDialog_content\\" class=\\"mx_CompoundDialog mx_ScrollableBaseDialog\\"><div class=\\"mx_CompoundDialog_header\\"><h1>Create poll</h1><div aria-label=\\"Close dialog\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_CompoundDialog_cancelButton\\"></div></div><form><div class=\\"mx_CompoundDialog_content\\"><div class=\\"mx_PollCreateDialog\\"><h2>What is your poll question or topic?</h2><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Question or topic\\" placeholder=\\"Write something...\\" type=\\"text\\" id=\\"mx_Field_4\\" value=\\"How many turnips is the optimal number?\\"><label for=\\"mx_Field_4\\">Question or topic</label></div><h2>Create options</h2><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 1\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_5\\" value=\\"As many as my neighbour\\"><label for=\\"mx_Field_5\\">Option 1</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 2\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_6\\" value=\\"The question is meaningless\\"><label for=\\"mx_Field_6\\">Option 2</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 3\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_7\\" value=\\"Mu\\"><label for=\\"mx_Field_7\\">Option 3</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_addOption mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary\\">Add option</div></div></div><div class=\\"mx_CompoundDialog_footer\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline\\">Cancel</div><button type=\\"submit\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_Dialog_nonDialogButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary\\">Create Poll</button></div></form></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div>"`;
|
||||||
|
|
||||||
|
exports[`PollCreateDialog renders info from a previous event 1`] = `"<div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\" role=\\"dialog\\" aria-labelledby=\\"mx_CompoundDialog_title\\" aria-describedby=\\"mx_CompoundDialog_content\\" class=\\"mx_CompoundDialog mx_ScrollableBaseDialog\\"><div class=\\"mx_CompoundDialog_header\\"><h1>Edit poll</h1><div aria-label=\\"Close dialog\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_CompoundDialog_cancelButton\\"></div></div><form><div class=\\"mx_CompoundDialog_content\\"><div class=\\"mx_PollCreateDialog\\"><h2>What is your poll question or topic?</h2><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Question or topic\\" placeholder=\\"Write something...\\" type=\\"text\\" id=\\"mx_Field_8\\" value=\\"Poll Q\\"><label for=\\"mx_Field_8\\">Question or topic</label></div><h2>Create options</h2><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 1\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_9\\" value=\\"Answer 1\\"><label for=\\"mx_Field_9\\">Option 1</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div class=\\"mx_PollCreateDialog_option\\"><div class=\\"mx_Field mx_Field_input mx_Field_labelAlwaysTopLeft mx_Field_placeholderIsHint\\"><input maxlength=\\"340\\" label=\\"Option 2\\" placeholder=\\"Write an option\\" type=\\"text\\" id=\\"mx_Field_10\\" value=\\"Answer 2\\"><label for=\\"mx_Field_10\\">Option 2</label></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_removeOption\\"></div></div><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_PollCreateDialog_addOption mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary\\">Add option</div></div></div><div class=\\"mx_CompoundDialog_footer\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline\\">Cancel</div><button type=\\"submit\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_Dialog_nonDialogButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary\\">Done</button></div></form></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div>"`;
|
||||||
|
|
|
@ -1110,8 +1110,8 @@ function newPollStart(
|
||||||
question = "What should we order for the party?";
|
question = "What should we order for the party?";
|
||||||
}
|
}
|
||||||
|
|
||||||
const answersFallback = Array.from(answers.entries())
|
const answersFallback = answers
|
||||||
.map(([i, a]) => `${i + 1}. ${a[M_TEXT.name]}`)
|
.map((a, i) => `${i + 1}. ${a[M_TEXT.name]}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
const fallback = `${question}\n${answersFallback}`;
|
const fallback = `${question}\n${answersFallback}`;
|
||||||
|
|
Loading…
Reference in a new issue