fix(ui): uxt fixes

This commit is contained in:
nick-delirium 2023-12-01 10:46:43 +01:00
parent 3a1fb49866
commit 8777026f69
7 changed files with 109 additions and 55 deletions

View file

@ -4,7 +4,7 @@ import { observer } from 'mobx-react-lite'
import { Typography, Switch, Button, Space } from "antd";
import { ExportOutlined } from "@ant-design/icons";
const SidePanel = observer(({ onSave, onPreview }: any) => {
const SidePanel = observer(({ onSave, onPreview, taskLen }: any) => {
const { uxtestingStore } = useStore();
return (
<div className={'flex flex-col gap-2 col-span-1'}>
@ -32,12 +32,12 @@ const SidePanel = observer(({ onSave, onPreview }: any) => {
</div>
</div>
<Button onClick={onPreview}>
<Button type={'primary'} ghost onClick={onPreview} disabled={taskLen === 0}>
<Space align={'center'}>
Preview <ExportOutlined rev={undefined} />
</Space>
</Button>
<Button type={'primary'} onClick={onSave}>
<Button type={'primary'} onClick={onSave} disabled={taskLen === 0}>
Publish Test
</Button>
</div>

View file

@ -1,10 +1,4 @@
import {
Button,
Input,
Typography,
Dropdown,
Modal,
} from 'antd';
import { Button, Input, Typography, Dropdown, Modal } from 'antd';
import React from 'react';
import {
withSiteId,
@ -21,6 +15,7 @@ import { useStore } from 'App/mstore';
import { confirm } from 'UI';
import StepsModal from './StepsModal';
import SidePanel from './SidePanel';
import usePageTitle from 'App/hooks/usePageTitle';
const menuItems = [
{
@ -36,20 +31,28 @@ const menuItems = [
];
function TestEdit() {
const { uxtestingStore } = useStore();
const [newTestTitle, setNewTestTitle] = React.useState('');
const [newTestDescription, setNewTestDescription] = React.useState('');
const [conclusion, setConclusion] = React.useState('');
const [guidelines, setGuidelines] = React.useState('');
const [isModalVisible, setIsModalVisible] = React.useState(false);
const { uxtestingStore } = useStore();
const [isConclusionEditing, setIsConclusionEditing] = React.useState(false);
const [isOverviewEditing, setIsOverviewEditing] = React.useState(false);
// @ts-ignore
const { siteId, testId } = useParams();
const { showModal, hideModal } = useModal();
const history = useHistory();
usePageTitle(`Usability Tests | ${uxtestingStore.instance ? 'Edit' : 'Create'}`);
React.useEffect(() => {
if (testId && testId !== 'new') {
uxtestingStore.getTestData(testId);
uxtestingStore.getTestData(testId).then((inst) => {
if (inst) {
setConclusion(inst.conclusionMessage || '');
setGuidelines(inst.guidelines || '');
}
});
}
}, []);
if (!uxtestingStore.instance) {
@ -105,7 +108,7 @@ function TestEdit() {
};
return (
<>
<div className="w-full mx-auto" style={{ maxWidth: '1360px' }}>
<Breadcrumb
items={[
{
@ -170,42 +173,52 @@ function TestEdit() {
uxtestingStore.instance!.setProperty('startingPath', e.target.value);
}}
/>
<Typography.Text>Test will begin on this page</Typography.Text>
<Typography.Text>Test will begin on this page.</Typography.Text>
</div>
<div className={'p-4 rounded bg-white border flex flex-col gap-2'}>
<Typography.Text strong>Introduction & Guidelines</Typography.Text>
<Typography.Text strong>Introduction and Guidelines for Participants</Typography.Text>
<Typography.Text></Typography.Text>
{isOverviewEditing ? (
<Input.TextArea
autoFocus
rows={5}
placeholder={'Task overview'}
value={uxtestingStore.instance.guidelines}
onChange={(e) => uxtestingStore.instance!.setProperty('guidelines', e.target.value)}
value={guidelines}
onChange={(e) => setGuidelines(e.target.value)}
/>
) : (
<Typography.Text>
<div className={'whitespace-pre-wrap'}>
{uxtestingStore.instance?.guidelines?.length
? uxtestingStore.instance.guidelines
: 'Provide an overview of this user test to and input guidelines that can be of assistance to users at any point during the test.'}
</Typography.Text>
: 'Provide an overview of this usability test to and input guidelines that can be of assistance to users at any point during the test.'}
</div>
)}
<div className={'flex gap-2'}>
{isOverviewEditing ? (
<>
<Button type={'primary'} onClick={() => setIsOverviewEditing(false)}>
<Button
type={'primary'}
onClick={() => {
uxtestingStore.instance!.setProperty('guidelines', guidelines);
setIsOverviewEditing(false);
}}
>
Save
</Button>
<Button
onClick={() => {
uxtestingStore.instance!.setProperty('guidelines', '');
setIsOverviewEditing(false);
setGuidelines(uxtestingStore.instance?.guidelines || '');
}}
>
Remove
Cancel
</Button>
</>
) : (
<Button onClick={() => setIsOverviewEditing(true)}>Add</Button>
<Button type={'primary'} ghost onClick={() => setIsOverviewEditing(true)}>
{uxtestingStore.instance?.guidelines?.length ? 'Edit' : 'Add'}
</Button>
)}
</div>
</div>
@ -238,6 +251,8 @@ function TestEdit() {
))}
<div>
<Button
type={'primary'}
ghost
onClick={() =>
showModal(
<StepsModal
@ -264,10 +279,8 @@ function TestEdit() {
{isConclusionEditing ? (
<Input.TextArea
placeholder={'Thanks for participation!..'}
value={uxtestingStore.instance!.conclusionMessage}
onChange={(e) =>
uxtestingStore.instance!.setProperty('conclusionMessage', e.target.value)
}
value={conclusion}
onChange={(e) => setConclusion(e.target.value)}
/>
) : (
<Typography.Text>{uxtestingStore.instance!.conclusionMessage}</Typography.Text>
@ -276,27 +289,39 @@ function TestEdit() {
<div className={'flex gap-2'}>
{isConclusionEditing ? (
<>
<Button type={'primary'} onClick={() => setIsConclusionEditing(false)}>
<Button
type={'primary'}
onClick={() => {
uxtestingStore.instance!.setProperty('conclusionMessage', conclusion);
setIsConclusionEditing(false);
}}
>
Save
</Button>
<Button
onClick={() => {
uxtestingStore.instance!.setProperty('conclusionMessage', '');
setIsConclusionEditing(false);
setConclusion(uxtestingStore.instance?.conclusionMessage || '');
}}
>
Remove
Cancel
</Button>
</>
) : (
<Button onClick={() => setIsConclusionEditing(true)}>Edit</Button>
<Button type={'primary'} ghost onClick={() => setIsConclusionEditing(true)}>
Edit
</Button>
)}
</div>
</div>
</div>
<SidePanel onSave={() => onSave(false)} onPreview={() => onSave(true)} />
<SidePanel
taskLen={uxtestingStore.instance.tasks.length}
onSave={() => onSave(false)}
onPreview={() => onSave(true)}
/>
</div>
</>
</div>
);
}

View file

@ -1,4 +1,5 @@
import { durationFormatted } from 'App/date';
import usePageTitle from "App/hooks/usePageTitle";
import { numberWithCommas } from 'App/utils';
import { getPdf2 } from 'Components/AssistStats/pdfGenerator';
import { useModal } from 'Components/Modal';
@ -64,11 +65,13 @@ function TestOverview() {
const { siteId, testId } = useParams();
const { showModal } = useModal();
const { uxtestingStore } = useStore();
usePageTitle(`Usability Tests | ${uxtestingStore.instance?.title || ''}`);
React.useEffect(() => {
uxtestingStore.getTest(testId);
}, [testId]);
if (!uxtestingStore.instance) {
return <Loader loading={uxtestingStore.isLoading}>No data.</Loader>;
}
@ -78,7 +81,7 @@ function TestOverview() {
};
return (
<>
<div className="w-full mx-auto" style={{ maxWidth: '1360px'}}>
<Breadcrumb
items={[
{
@ -172,7 +175,7 @@ function TestOverview() {
</Loader>
</div>
</div>
</>
</div>
);
}
@ -378,7 +381,7 @@ const Title = observer(({ testId, siteId }: any) => {
</div>
}
>
<Button>
<Button type={'primary'} ghost>
<Space align={'center'}>
Distribute
<ShareAltOutlined rev={undefined} />
@ -386,7 +389,7 @@ const Title = observer(({ testId, siteId }: any) => {
</Button>
</Popover>
<Dropdown menu={{ items: menuItems, onClick: onMenuClick }}>
<Button icon={<MoreOutlined rev={undefined} />}></Button>
<Button ghost type={'primary'} icon={<MoreOutlined rev={undefined} />}></Button>
</Dropdown>
</div>
)

View file

@ -12,16 +12,17 @@ import { UnorderedListOutlined, ArrowRightOutlined } from '@ant-design/icons';
import { useHistory, useParams } from 'react-router-dom';
import { withSiteId, usabilityTestingEdit, usabilityTestingView } from 'App/routes';
import { debounce } from 'App/utils';
import withPageTitle from 'HOCs/withPageTitle';
const { Search } = Input;
const PER_PAGE = 10;
let debouncedSearch: any = () => null
const defaultDescription = `To evaluate the usability of [Feature Name], focusing on user interaction, efficiency, and satisfaction. The aim is to identify any usability issues that users may encounter, understand how they navigate [Feature Name], and gauge the intuitiveness of the workflow.`
function TestsTable() {
const [newTestTitle, setNewTestTitle] = React.useState('');
const [newTestDescription, setNewTestDescription] = React.useState('');
const [newTestDescription, setNewTestDescription] = React.useState(defaultDescription);
const [isModalVisible, setIsModalVisible] = React.useState(false);
const { uxtestingStore } = useStore();
@ -63,7 +64,7 @@ function TestsTable() {
};
return (
<>
<div className="w-full mx-auto" style={{ maxWidth: '1360px'}}>
<Modal
title="Create Usability Test"
open={isModalVisible}
@ -78,8 +79,9 @@ function TestsTable() {
</Button>
}
>
<Typography.Text strong>Name this user test</Typography.Text>
<Typography.Text strong>Title</Typography.Text>
<Input
autoFocus
placeholder="E.g. Checkout user journey evaluation"
style={{ marginBottom: '2em' }}
value={newTestTitle}
@ -87,6 +89,7 @@ function TestsTable() {
/>
<Typography.Text strong>Test Objective (optional)</Typography.Text>
<Input.TextArea
rows={5}
value={newTestDescription}
onChange={(e) => setNewTestDescription(e.target.value)}
placeholder="Share a brief statement about what you aim to discover through this study."
@ -126,7 +129,7 @@ function TestsTable() {
<AnimatedSVG name={ICONS.NO_FFLAGS} size={285} />
<div className="text-center text-gray-600 mt-4">
{uxtestingStore.searchQuery === ''
? "You haven't created any user tests yet"
? "You haven't created any usability tests yet"
: 'No matching results'}
</div>
</div>
@ -161,7 +164,7 @@ function TestsTable() {
)}
</div>
</div>
</>
</div>
);
}
@ -202,4 +205,4 @@ function Cell({ size, children }: { size: number; children?: React.ReactNode })
return <div className={`col-span-${size}`}>{children}</div>;
}
export default observer(TestsTable);
export default withPageTitle('Usability Tests')(observer(TestsTable))

View file

@ -27,6 +27,27 @@ interface Response {
duration: number;
}
const defaultGuidelines = `
Introduction:localStorage
Thank you for participating in this important stage of our [Website/App] development. Your insights will help us enhance the [Desktop/Mobile] experience for all users.localStoragelocalStorage
Before You Begin:localStorage
Device: Ensure you're using a [Desktop/Mobile] for this test.localStoragelocalStorage
Environment: Choose a quiet location where you can focus without interruptions.localStoragelocalStorage
Test Guidelines:localStorage
1. Task Flow: You will perform a series of tasks as you normally would when using a [Website/App].localStoragelocalStorage
2. Think Aloud: Please verbalize your thoughts. If something is confusing, interesting, or pleasing, let us know.localStoragelocalStorage
3. No Right or Wrong: There are no correct answers here, just your honest experience.localStoragelocalStorage
4. Pace Yourself: Take your time, there's no rush to complete the tasks quickly.localStoragelocalStorage
5. Technical Issues: If you encounter any issues, please describe what you were attempting to do when the issue occurred.localStorage
`
export default class UxtestingStore {
client = uxtestingService;
tests: UxTListEntry[] = [];
@ -125,7 +146,7 @@ export default class UxtestingStore {
requireMic: false,
requireCamera: false,
description: description,
guidelines: '',
guidelines: defaultGuidelines,
conclusionMessage: '',
visibility: true,
tasks: [],
@ -185,6 +206,7 @@ export default class UxtestingStore {
try {
const test = await this.client.fetchTest(testId);
this.setInstance(new UxTestInst(test));
return this.instance
} catch (e) {
console.error(e);
} finally {

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "11.0.0-beta.1",
"version": "11.0.0-beta.4",
"keywords": [
"logging",
"replay"

View file

@ -199,7 +199,7 @@ export default class UserTestManager {
)
this.removeGreeting = () => {
this.container.innerHTML = ''
// this.container.innerHTML = ''
if (micRequired || cameraRequired) {
void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired)
}
@ -213,7 +213,7 @@ export default class UserTestManager {
this.removeGreeting()
this.durations.testStart = this.app.timestamp()
void this.signalTest('begin')
this.showWidget(this.test?.description || '', this.test?.tasks || [])
this.showWidget(this.test?.guidelines || '', this.test?.tasks || [])
}
this.container.append(titleElement, descriptionElement, noticeElement, buttonElement)
@ -222,7 +222,7 @@ export default class UserTestManager {
}
showWidget(
description: string,
guidelines: string,
tasks: {
title: string
description: string
@ -248,7 +248,7 @@ export default class UserTestManager {
// Create title section
const titleSection = this.createTitleSection()
Object.assign(this.container.style, styles.containerWidgetStyle)
const descriptionSection = this.createDescriptionSection(description)
const descriptionSection = this.createDescriptionSection(guidelines)
const tasksSection = this.createTasksSection(tasks)
const stopButton = createElement('div', 'stop_bn_or', styles.stopWidgetStyle, 'Abort Session')
@ -326,7 +326,7 @@ export default class UserTestManager {
// eslint-disable-next-line @typescript-eslint/no-empty-function
toggleDescriptionVisibility = () => {}
createDescriptionSection(description: string) {
createDescriptionSection(guidelines: string) {
const section = createElement('div', 'description_section_or', styles.descriptionWidgetStyle)
const titleContainer = createElement('div', 'description_s_title_or', styles.sectionTitleStyle)
const title = createElement('div', 'title', {}, 'Introduction & Guidelines')
@ -334,9 +334,10 @@ export default class UserTestManager {
const content = createElement('div', 'content', styles.contentStyle)
const descriptionC = createElement('div', 'text_description', {
maxHeight: '250px',
overflow: 'scroll',
overflowY: 'auto',
whiteSpace: 'pre-wrap',
})
descriptionC.innerHTML = description
descriptionC.innerHTML = guidelines
const button = createElement('div', 'button_begin_or', styles.buttonWidgetStyle, 'Begin Test')
titleContainer.append(title, icon)
@ -543,7 +544,7 @@ export default class UserTestManager {
'end_description_or',
{},
this.test?.conclusion ??
'Thank you for participating in our user test. Your feedback has been captured and will be used to enhance our website. \n' +
'Thank you for participating in our usability test. Your feedback has been captured and will be used to enhance our website. \n' +
'\n' +
'We appreciate your time and valuable input.',
)