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

feat: autocomplete data fields in the editor #4062

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

jessicamcinchak
Copy link
Member

@jessicamcinchak jessicamcinchak commented Dec 10, 2024

Key changes:

  • Introduces a new store method getFlowSchema which gets all the existing data fields (fn & val) for a given flow split into "nodes" (what you can click on in the graph) and "options" (Question & Checklist "options")
    • This allows us to have more refined suggestions when autocompleting and encourages content best practices
    • Filters and their flag values are omitted from the schema "suggestions" because they are not exposed to editors to configure
  • Adds a <DataFieldAutocomplete /> component which we can plug in across the editor like other input rows
  • Implements the autocomplete across all component types with editor-facing data fields, deriving schema "suggestions" using three broad strategies:
    1. Question & Checklist are most complex
      • Top-level fn suggestions reflect flow nodes, options val suggestions reflect other options only
      • If fn is any of proposal.projectType, property.type, or application.type, we'll override default suggested options in favor of ODP Schema enum values
        • When using ODP Schema values, any existing initialValues are additionally supported, as well as option to "Add.." new values, to ensure editors maintain flexibility and are able to use and experiment with data values outside of the current schema release
    2. FileUpload & FileUploadAndLabel
      • Suggestions override current flow schema and always suggest based on ODP Schema FileType enums; same initialValues & "Add.." new values handling as above
    3. All other component types (inputs, lists, etc)
      • Suggestions reflect other flow nodes only (never options)

Todos:

  • Tests
  • Plug in across all component types with configurable data fields:
    • AddressInput
    • Calculate
    • Checklist
    • ContactInput
    • DateInput
    • FileUpload
    • FileUploadAndLabel
    • List
    • MapAndLabel
    • NumberInput
    • Page
    • Question
    • SetValue
    • TextInput

https://trello.com/c/6OkLrUNE/2668-autofill-passport-variables-when-creating-nodes-in-the-editor

Copy link

github-actions bot commented Dec 10, 2024

Pizza

Deployed 45c0221 to https://4062.planx.pizza.

Useful links:

return formattedOption;
}}
renderOption={renderOptions}
freeSolo
Copy link
Member Author

Choose a reason for hiding this comment

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

Explicitly passing the freeSolo prop is a bit strange because it's already handled in AutocompleteProps<string, false, false, true, "div">, but explicitly passing here seems to be the Stack Overflow consensus for how to clear up a console error related to isOptionEqualToValue which assumes "new" options will never be added/present

id="data-field-autocomplete"
key="data-field-autocomplete"
placeholder="Data field"
required={Boolean(props.required)}
Copy link
Member Author

@jessicamcinchak jessicamcinchak Dec 18, 2024

Choose a reason for hiding this comment

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

required prop will simply trigger browser default error handling which is quite consistent with current editor inputs !

Gov UK-looking error wrappers for autocomplete in the editor are larger future thing to revisit (eg would we simply wrap in our existing wrapper or want to account for error-related props here?)
Screenshot from 2024-12-18 10-13-06

Copy link
Contributor

Choose a reason for hiding this comment

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

This is what I would have gone for here as well! Currently we have a mix but this works great. We can revisit this when taking another design look at this modal alongside labels and layout.

backgroundColor: theme.palette.background.paper,
[`& .${outlinedInputClasses.root}, input`]: {
cursor: "pointer",
// TODO extract as `format="data"` prop more like `Input` ?
Copy link
Member Author

Choose a reason for hiding this comment

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

This TODO is good to note, but I don't think we need to pick it up until there's another use case/iteration of the autocomplete input - any objections?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep good point - I don't think we really need a format prop here right now - a TODO works for me!

@@ -92,6 +94,8 @@ function ListComponent(props: Props) {
validateOnChange: false,
});

const dataFieldSchema = useStore().getFlowSchema()?.nodes;
Copy link
Member Author

Choose a reason for hiding this comment

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

List, Page & MapAndLabel components have their own pre-existing concepts of "schemas", so the store function is more explicitly assigned to dataFieldSchema in these cases


// TODO align to (reuse?) data facets from search
if (node.data?.output) nodes.add(node.data.output);
if (node.data?.dataFieldBoundary) nodes.add(node.data.dataFieldBoundary);
Copy link
Member Author

@jessicamcinchak jessicamcinchak Dec 18, 2024

Choose a reason for hiding this comment

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

This TODO goes away with #4083 & #4085 🧹

await page.getByPlaceholder("Data Field").fill(options?.[0] || "");
await page
.getByRole("combobox", { name: "Data field" })
.fill(options?.[0] || "");
Copy link
Member Author

@jessicamcinchak jessicamcinchak Dec 19, 2024

Choose a reason for hiding this comment

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

‼️ E2E still are ultimately failing because nodes with a required data field are not being correctly populated - I've tried a couple iterations here, but still not landed on the right solution.

Opening for wider review in the meantime and any pointers very welcome!

Here's what I've tried so far:

  • The placeholder text has changed from "Data Field" to "Data field", but getByPlaceholder() will now match more than one element (MUI Autocompletes have many nested elements)
    Screenshot from 2024-12-18 21-46-20
  • So, "getByRole("combobox", { name: "Data field" }) should exclusively be matching the input element
  • On the first component, there will be no flow schema or suggestions yet and the popupIcon arrow is disabled, so there's no dropdown of options to .click() or open/expand - but I'm guessing I'm still using the wrong combo of .fill/.click here to mimic typing input ??

Copy link
Contributor

Choose a reason for hiding this comment

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

This is working for me locally - 5114475

Running on CI now 🤞

Copy link
Contributor

Choose a reason for hiding this comment

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

It failed on some still - to be fair I didn't run them all locally just the first few. Will pick this up again but I think adding the .click() is what we're after.

Copy link
Member Author

Choose a reason for hiding this comment

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

Appreciate second eyes on this - it's a tedious tricky one! Once one node with a data field is successfully added, then future ones will have suggested schema options - so might require combination of fill & click ?

@jessicamcinchak jessicamcinchak marked this pull request as ready for review December 19, 2024 07:31
@jessicamcinchak jessicamcinchak requested a review from a team December 19, 2024 07:31

const schema = useStore().getFlowSchema()?.options;
const initialOptions: Option[] | undefined = formik.initialValues.options || formik.initialValues.groupedOptions?.map((group: Group<Option>) => group.children)?.flat();
const initialOptionVals = initialOptions?.map((option) => option.data?.val);
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a bit of a lazy rebase right now while the exclusive-or work is still in-progress - I think initialOptions should be able to be simplified/derived from exclusiveOptions or nonExclusiveOptions once those are handling "grouped" options

Copy link
Contributor

@DafyddLlyr DafyddLlyr left a comment

Choose a reason for hiding this comment

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

Great! Zero issues - all working as expected, code was clear and easy to follow.

Appreciate the detailed PR description and thought that's gone into this - great feature 😄

import InputRow from "ui/shared/InputRow";

interface Props {
schema?: string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

Could be have getStore().getFlowSchema()?.nodes be the default value for this prop so that it doesn't need to be defined in many components?

It also then feel more explicit where there's an exception (e.g. for options).

Copy link
Member Author

Choose a reason for hiding this comment

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

💡 Good suggestion! Will aim to push this change up this afternoon

Copy link
Member Author

Choose a reason for hiding this comment

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

Very satisfying one to implement ✂️ thanks again ! 867a6c2

backgroundColor: theme.palette.background.paper,
[`& .${outlinedInputClasses.root}, input`]: {
cursor: "pointer",
// TODO extract as `format="data"` prop more like `Input` ?
Copy link
Contributor

Choose a reason for hiding this comment

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

Yep good point - I don't think we really need a format prop here right now - a TODO works for me!

id="data-field-autocomplete"
key="data-field-autocomplete"
placeholder="Data field"
required={Boolean(props.required)}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is what I would have gone for here as well! Currently we have a mix but this works great. We can revisit this when taking another design look at this modal alongside labels and layout.

@@ -63,3 +64,21 @@ export const getPreviouslySubmittedData = ({

return data;
};

export const getOptionsSchemaByFn = (fn?: string, defaultOptionsSchema?: string[], initialOptions?: (string | undefined)[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Super clear and easy to follow 👍

if (node.data?.fn !== "flag") nodes.add(node.data.fn)
};

// TODO align to (reuse?) data facets from search
Copy link
Contributor

Choose a reason for hiding this comment

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

Great comment!

We could probably use DATA_FACETS here (filtered to strings only) and then use this to get the data values from each node.

However, I think this is actually a more complex solution - there's a very different use case here and we'll have just as many exceptions and clauses. This works really well for now - we just need to make sure that we keep data values for new components, this function, and DATA_FACETS in sync.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep - fn only basically felt like right "first" go at this since other DATA_FACETS like FileUploadAndLabel individual file fns are getting "overwritten" by ODP Schema enum suggestions anyways ! So it's a good question of whether or not we actually always want this to be perfectly in sync !

@DafyddLlyr
Copy link
Contributor

P.S. Happy to have a crack at Playwright later today and push a commit here 👍

@DafyddLlyr DafyddLlyr force-pushed the jess/data-field-autocomplete branch from 710156f to ec9c832 Compare December 20, 2024 16:34
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.

2 participants