Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#81 (1) JsonForms skeleton #148

Merged
merged 21 commits into from
Jun 22, 2022
Merged

#81 (1) JsonForms skeleton #148

merged 21 commits into from
Jun 22, 2022

Conversation

CarlosNZ
Copy link
Contributor

@CarlosNZ CarlosNZ commented Jun 20, 2022

This just provides the basic skeleton for the JSON forms as outlined here.

Suggest we use this as a base for subsequent PRs, then merge them as one once this area is "complete"

Will create issues for remaining f/e items from #81.

Here we have:

  • the JsonForms package
  • the useJsonForms hook
  • 5 initial custom components and their testers for over-riding default JsonForm UI elements:
    • Text
    • Selector (drop-down), for enums
    • Group
    • Label (for text display, non interactive)
    • Date picker
    • I got rather bogged down trying to get an Array component to work properly, but haven't solved that yet, although I have a new avenue to try next: https://jsonforms.discourse.group/t/overriding-array-expand-panel/827. But they currently look pretty ugly with the default renderer.
  • some hard-coded JSON data for demoing -- schema, uiSchema and "Patient" data, will be replaced with database queries once back-end document endpoint is ready

To test:

Just click on a name in the Patient list and the Modal will display the hard-coded "Patient" data using the associated schemas. There's a lot of clutter in the form currently, due to the default Array component.

@CarlosNZ CarlosNZ mentioned this pull request Jun 20, 2022
16 tasks
Comment on lines 20 to 21
"@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^5.8.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the icons, I'll try and remove in next PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please! the icons packages is huge and we have deliberately created only the icons we need in order to avoid it

@@ -8,6 +8,7 @@ interface ButtonProps {
label: string;
width?: string;
height?: string;
color?: 'primary' | 'secondary' | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll notice this isn't used in this PR -- I'm using it for the Arrays to create the "Add item" button -- in next PR

Comment on lines 56 to 63
stringSubstitution: (
parameterisedString: string,
data: { [key: string]: any },
fallback = 'Not found'
) =>
parameterisedString.replace(/\${(.*?)}/gm, (_: string, match: string) =>
extractProperty(data, match, fallback)
),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little copy-paste from Conforma ;)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

and.. oh, right. the name stringSubstitution referenced earlier did not make me expect this kind of functionality. Something like 'formatTemplateString' or 'templateSubstitution' 🤷 dunno

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point, I've never been particularly happy with that name. Changed it to formatTemplateString

@mark-prins
Copy link
Collaborator

Suggest we use this as a base for subsequent PRs, then merge them as one once this area is "complete"

Don't see any reason to delay while waiting for a fully feature complete version. The demo server for the feature site has the webhook disabled, so will only be updated manually.

Copy link
Collaborator

@mark-prins mark-prins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

That looks great! very strong start - form looks good.

One extra thing I wondered about is the massive (1) (2) etc - some time.. can they be made more subtle please?

image

import { FormLabel, Box } from '@mui/material';
import { BaseDatePickerInput } from '@openmsupply-client/common';

export const dateTester = rankWith(5, isDateControl);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a new user to this - what is a tester?
Can you add a comment to explain?
Could they be grouped in a testers file? Would that make it clearer? or is it helpful to have them next to the component?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tester -- function that returns a number based on whether or not a predicate is true (In this case it will return 5 if isDateControl, otherwise it returns -1). Then for each form element, the tester that returns the highest number gets rendered by the component matched to that tester (in "renderers" prop), i.e.

{ tester: stringTester, renderer: TextField },
{ tester: selectTester, renderer: Selector },
{ tester: groupTester, renderer: Group },
{ tester: labelTester, renderer: Label },
{ tester: dateTester, renderer: Date },
{ tester: arrayTester, renderer: Array }

I thought since we're generally going to have testers coupled to components in a one-to-one manner, it made sense to keep each tester with its associated component.

I did make a more generic tester for matching custom fields (in uiSchema) to components, but it wasn't being used in here. I might include it in a later PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, right. thanks - wasn't clear to me from the name alone!

<BaseDatePickerInput
value={data}
onChange={e => handleChange(path, e)}
inputFormat="dd/MM/yyyy"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hard-coded format : can it be intl based? can it be configurable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually just copied straight from common/src/ui/components/inputs/DatePickers/DatePickerInput/DatePickerInput.tsx 😉

So yeah, we should localise them both at some point.

import { MaterialLayoutRenderer } from '@jsonforms/material-renderers';
import { Box, Typography } from '@mui/material';

export const groupTester = rankWith(4, uiTypeIs('Group'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, these things aren't making sense. what is 4 here?

marginBottom: 2,
}}
>
<Typography width="40%" fontSize="1.2em" textAlign="right">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may have to use ems throughout 🤔

Could the font size here come from the theme?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, have re-styled the "subtitle" variant in theme with this fontSize -- didn't look like it was being used elsewhere.

Would like to organise the typography a bit better overall at some point: msupply-foundation/openmsupply-client#1149

return (
<Box
sx={{
maxWidth: 500,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the maxWidth is fairly specific, just wondering if that will cause problems later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved these values into a "styleConstants" file so we can update them easily if we need to.

Also -- if we need a wider "Group" in future, we can always make "width" a new property in uiSchema which the component can use, or have an alternative component "WideGroup". (for one that spans 2 columns, perhaps?)

Comment on lines 56 to 63
stringSubstitution: (
parameterisedString: string,
data: { [key: string]: any },
fallback = 'Not found'
) =>
parameterisedString.replace(/\${(.*?)}/gm, (_: string, match: string) =>
extractProperty(data, match, fallback)
),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

and.. oh, right. the name stringSubstitution referenced earlier did not make me expect this kind of functionality. Something like 'formatTemplateString' or 'templateSubstitution' 🤷 dunno

if (isLoading) return <BasicSpinner />;

return !!data ? (
return !error ? (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controversial maybe.. flip the logic?

return error ? null : (<blah />);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sure -- that's just a small personal preference -- in ternarys I generally like to put the "expected" or "preferred" outcome first so the "bad" outcome is tucked nicely out of the way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah me too - and then I fight with the desire to put the positive assertion in the conditional - preferring if error to if !error - and also for having the small bit first, so that it's easier to read. In cases like this where the first condition is big, it's hard to see the little null.
as you were then.. real no need to change

client/packages/system/src/Patient/api/operations.graphql Outdated Show resolved Hide resolved
@@ -9,8 +9,8 @@ import {
Fade,
NothingHere,
createQueryParamsStore,
useFormatDateTime,
ColumnAlign,
// useFormatDateTime,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add the DOB back in? It's working and wanted, and I'm not sure why it is removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@CarlosNZ
Copy link
Contributor Author

One extra thing I wondered about is the massive (1) (2) etc - some time.. can they be made more subtle please?

Yes, that's what I've been trying to fix with the Array component. I've made a much nicer, more consistent UI using an Accordion -- but I still haven't cracked how to reliably (I can do it unreliably lol!) pass everything through to the inner elements. So that'll be in another PR.

Copy link
Contributor

@clemens-msupply clemens-msupply left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only had a look at the UI and looks great! yes please go ahead and merge I will have a more detailed look later

@CarlosNZ CarlosNZ merged commit 483b2e4 into feature/programs Jun 22, 2022
@CarlosNZ CarlosNZ deleted the JSON-forms branch June 22, 2022 02:58
@andreievg andreievg mentioned this pull request Jun 27, 2022
25 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants