Skip to content

Commit 7d8f635

Browse files
committed
Fix Dialogue form #308
1 parent 636278a commit 7d8f635

File tree

8 files changed

+116
-91
lines changed

8 files changed

+116
-91
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This changelog covers all three packages, as they are (for now) updated as a who
1212
- Add new file preview UI for images, audio, text and PDF files.
1313
- Add new file preview types to the folder grid view.
1414
- Add `store.preloadClassesAndProperties` and remove `urls.properties.getAll` and `urls.classes.getAll`. This enables using `atomic-data-browser` without relying on `atomicdata.dev` being available.
15+
- Fix Dialogue form #308
1516

1617
## v0.35.0
1718

data-browser/src/components/forms/NewForm/index.tsx renamed to data-browser/src/components/forms/NewForm/NewFormDialog.tsx

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,24 @@
11
import { properties, useResource, useStore, useTitle } from '@tomic/react';
2-
import React, { useCallback, useState } from 'react';
3-
import { useQueryString } from '../../../helpers/navigation';
2+
import React, { useState, useCallback } from 'react';
43
import { useEffectOnce } from '../../../hooks/useEffectOnce';
54
import { Button } from '../../Button';
6-
import { DialogActions, DialogContent, DialogTitle } from '../../Dialog';
5+
import { DialogTitle, DialogContent, DialogActions } from '../../Dialog';
76
import { ErrorLook } from '../../ErrorLook';
87
import { useSaveResource } from '../hooks/useSaveResource';
98
import { InlineErrMessage } from '../InputStyles';
109
import { ResourceForm, ResourceFormVariant } from '../ResourceForm';
10+
import { NewFormProps } from './NewFormPage';
1111
import { NewFormTitle, NewFormTitleVariant } from './NewFormTitle';
1212
import { SubjectField } from './SubjectField';
1313
import { useNewForm } from './useNewForm';
1414

15-
export interface NewFormProps {
16-
classSubject: string;
17-
}
18-
1915
export interface NewFormDialogProps extends NewFormProps {
2016
closeDialog: () => void;
2117
initialTitle: string;
2218
onSave: (subject: string) => void;
2319
parent: string;
2420
}
2521

26-
/** Fullpage Form for instantiating a new Resource from some Class */
27-
export const NewFormFullPage = ({
28-
classSubject,
29-
}: NewFormProps): JSX.Element => {
30-
const klass = useResource(classSubject);
31-
const [subject, setSubject] = useQueryString('newSubject');
32-
const [parentSubject] = useQueryString('parent');
33-
34-
const { subjectErr, subjectValue, setSubjectValue, resource } = useNewForm(
35-
klass,
36-
subject,
37-
setSubject,
38-
parentSubject,
39-
);
40-
41-
return (
42-
<>
43-
<NewFormTitle classSubject={classSubject} />
44-
<SubjectField
45-
error={subjectErr}
46-
value={subjectValue}
47-
onChange={setSubjectValue}
48-
/>
49-
{/* Key is required for re-rendering when subject changes */}
50-
<ResourceForm
51-
resource={resource}
52-
classSubject={classSubject}
53-
key={`${classSubject}+${subject}`}
54-
/>
55-
</>
56-
);
57-
};
58-
5922
/** Form for instantiating a new Resource from some Class in a Modal / Dialog view */
6023
export const NewFormDialog = ({
6124
classSubject,
@@ -72,12 +35,12 @@ export const NewFormDialog = ({
7235

7336
const [subject, setSubject] = useState(store.createSubject());
7437

75-
const { subjectErr, subjectValue, setSubjectValue, resource } = useNewForm(
38+
const { subjectErr, subjectValue, setSubjectValue, resource } = useNewForm({
7639
klass,
77-
subject,
7840
setSubject,
41+
initialSubject: subject,
7942
parent,
80-
);
43+
});
8144

8245
const onResourceSave = useCallback(() => {
8346
onSave(resource.getSubject());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useResource } from '@tomic/react';
2+
import { useQueryString } from '../../../helpers/navigation';
3+
import { ResourceForm } from '../ResourceForm';
4+
import { NewFormTitle } from './NewFormTitle';
5+
import { SubjectField } from './SubjectField';
6+
import { useNewForm } from './useNewForm';
7+
import React from 'react';
8+
9+
export interface NewFormProps {
10+
classSubject: string;
11+
}
12+
13+
/** Fullpage Form for instantiating a new Resource from some Class */
14+
export const NewFormFullPage = ({
15+
classSubject,
16+
}: NewFormProps): JSX.Element => {
17+
const klass = useResource(classSubject);
18+
const [subject, setSubject] = useQueryString('newSubject');
19+
const [parentSubject] = useQueryString('parent');
20+
21+
const { subjectErr, subjectValue, setSubjectValue, resource } = useNewForm({
22+
klass,
23+
setSubject,
24+
initialSubject: subject,
25+
parent: parentSubject,
26+
});
27+
28+
return (
29+
<>
30+
<NewFormTitle classSubject={classSubject} />
31+
<SubjectField
32+
error={subjectErr}
33+
value={subjectValue}
34+
onChange={setSubjectValue}
35+
/>
36+
{/* Key is required for re-rendering when subject changes */}
37+
<ResourceForm
38+
resource={resource}
39+
classSubject={classSubject}
40+
key={`${classSubject}+${subject}`}
41+
/>
42+
</>
43+
);
44+
};

data-browser/src/components/forms/NewForm/useNewForm.ts

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,63 @@ import {
66
useResource,
77
useArray,
88
} from '@tomic/react';
9-
import { useState, useEffect, useDeferredValue } from 'react';
9+
import { useState, useEffect } from 'react';
1010

1111
const resourseOpts = { newResource: true };
1212

13+
type UseNewForm = {
14+
klass: Resource;
15+
setSubject: (v: string) => void;
16+
initialSubject?: string;
17+
parent?: string;
18+
};
19+
1320
/** Shared logic for NewForm components. */
14-
export const useNewForm = (
15-
klass: Resource,
16-
subject: string | undefined,
17-
setSubject: (v: string) => void,
18-
parent?: string,
19-
) => {
21+
export const useNewForm = (args: UseNewForm) => {
22+
const { klass, setSubject, initialSubject, parent } = args;
23+
2024
const store = useStore();
21-
// TODO: Don't push to history, but replace, because currenlty back is broken
2225
const [klassShortname] = useString(klass, properties.shortname);
23-
const [subjectValue, setSubjectValue] = useState<string>(() => {
24-
if (subject === undefined) {
26+
const [subjectValue, setSubjectValueInternal] = useState<string>(() => {
27+
if (initialSubject === undefined) {
2528
return store.createSubject(klassShortname);
2629
}
2730

28-
return subject;
31+
return initialSubject;
2932
});
3033

3134
const [subjectErr, setSubjectErr] = useState<Error | undefined>(undefined);
3235
const resource = useResource(subjectValue, resourseOpts);
33-
const [_, setParent] = useString(resource, properties.parent);
36+
const [parentVal, setParent] = useString(resource, properties.parent);
37+
const [isAVal, setIsA] = useArray(resource, properties.isA);
3438

39+
// When the resource is created or updated, make sure that the parent and class are present
3540
useEffect(() => {
36-
setParent(parent);
37-
}, [parent]);
38-
39-
// Set the class for new resources
40-
const [currentClass] = useArray(resource, properties.isA);
41-
42-
if (currentClass.length === 0) {
43-
resource.set(properties.isA, [klass.getSubject()], store);
44-
}
41+
if (parentVal !== parent) {
42+
setParent(parent);
43+
}
4544

46-
const defferedSubjectValue = useDeferredValue(subjectValue);
47-
/** Changes the URL of a subject. Updates the store */
48-
// Should be debounced as it is quite expensive, but getting that to work turned out to be really hard
49-
useEffect(() => {
50-
if (!defferedSubjectValue) {
51-
return;
45+
if (isAVal.length === 0) {
46+
setIsA([klass.getSubject()]);
5247
}
48+
}, [resource, parent]);
5349

54-
const oldSubject = resource.getSubject();
50+
async function setSubjectValue(newSubject: string) {
51+
setSubjectValueInternal(newSubject);
52+
setSubjectErr(undefined);
53+
setSubject(newSubject);
5554

56-
if (oldSubject === defferedSubjectValue) {
55+
if (resource.get(properties.parent) !== parent) {
56+
// This prevents that we move an empty temporary resource
5757
return;
5858
}
5959

60-
setSubjectErr(undefined);
61-
// Expensive!
62-
store
63-
.renameSubject(oldSubject, defferedSubjectValue)
64-
.then(() => {
65-
setSubject(defferedSubjectValue);
66-
})
67-
.catch(e => setSubjectErr(e));
68-
}, [defferedSubjectValue, resource]);
60+
try {
61+
await store.renameSubject(resource, newSubject);
62+
} catch (e) {
63+
setSubjectErr(e);
64+
}
65+
}
6966

7067
return {
7168
subjectErr,

data-browser/src/components/forms/ResourceSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import { ErrMessage, InputWrapper } from './InputStyles';
1717
import { DropdownInput } from './DropdownInput';
1818
import { Dialog, useDialog } from '../Dialog';
1919
import { DialogTreeContext } from '../Dialog/dialogContext';
20-
import { NewFormDialog } from './NewForm';
2120
import { useSettings } from '../../helpers/AppSettings';
2221
import styled from 'styled-components';
22+
import { NewFormDialog } from './NewForm/NewFormDialog';
2323

2424
interface ResourceSelectorProps {
2525
/**

data-browser/src/routes/NewRoute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import { ResourceSelector } from '../components/forms/ResourceSelector';
1313
import { Button } from '../components/Button';
1414
import { useSettings } from '../helpers/AppSettings';
1515
import { Row } from '../components/Row';
16-
import { NewFormFullPage } from '../components/forms/NewForm/index';
1716
import { ResourceInline } from '../views/ResourceInline';
1817
import styled from 'styled-components';
1918
import { FileDropzoneInput } from '../components/forms/FileDropzone/FileDropzoneInput';
2019
import toast from 'react-hot-toast';
2120
import { getIconForClass } from '../views/FolderPage/iconMap';
21+
import { NewFormFullPage } from '../components/forms/NewForm/NewFormPage';
2222

2323
/** Start page for instantiating a new Resource from some Class */
2424
function New(): JSX.Element {

data-browser/tests/e2e.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,13 @@ test.describe('data-browser', async () => {
556556
await newDrive(page);
557557
// Create new class from new resource menu
558558
await newResource('class', page);
559+
560+
await fillInput('shortname', page);
561+
await fillInput('description', page);
562+
await page.click('[data-test="save"]');
563+
// go back browser history
564+
await page.goBack();
565+
559566
await page
560567
.locator('[title="Add an item to this list"] >> nth=0')
561568
.first()
@@ -580,6 +587,7 @@ test.describe('data-browser', async () => {
580587
.fill('This is a test prop');
581588
await page.locator('dialog footer >> text=Save').click();
582589

590+
await page.locator('text=Resource Saved');
583591
expect(
584592
await page.locator(
585593
'[data-test="input-recommends"] >> nth=0 >> "test-prop"',
@@ -710,3 +718,18 @@ async function editTitle(title: string, page: Page) {
710718
async function clickSidebarItem(text: string, page: Page) {
711719
await page.click(`[data-test="sidebar"] >> text="${text}"`);
712720
}
721+
722+
async function fillInput(
723+
propertyShortname: string,
724+
page: Page,
725+
value?: string,
726+
) {
727+
let locator = `[data-test="input-${propertyShortname}"]`;
728+
729+
if (propertyShortname === 'description') {
730+
locator = 'textarea[name="yamdeContent"]';
731+
}
732+
733+
await page.click(locator);
734+
await page.fill(locator, value || `test-${propertyShortname}`);
735+
}

lib/src/store.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -494,22 +494,19 @@ export class Store {
494494
* errors if so.
495495
*/
496496
public async renameSubject(
497-
oldSubject: string,
497+
resource: Resource,
498498
newSubject: string,
499499
): Promise<void> {
500500
Client.tryValidSubject(newSubject);
501-
const old = this.resources.get(oldSubject);
502-
503-
if (old === undefined) {
504-
throw Error(`Old subject does not exist in store: ${oldSubject}`);
505-
}
501+
const oldSubject = resource.getSubject();
506502

507503
if (await this.checkSubjectTaken(newSubject)) {
508504
throw Error(`New subject name is already taken: ${newSubject}`);
509505
}
510506

511-
old.setSubject(newSubject);
512-
this.resources.set(newSubject, old);
507+
resource.setSubject(newSubject);
508+
this.addResources(resource);
509+
this.resources.set(newSubject, resource);
513510
this.removeResource(oldSubject);
514511
}
515512

0 commit comments

Comments
 (0)