Skip to content

OBPIH-6395 Add reusable useWizard hook#4642

Merged
awalkowiak merged 6 commits intodevelopfrom
OBPIH-6395
Jun 3, 2024
Merged

OBPIH-6395 Add reusable useWizard hook#4642
awalkowiak merged 6 commits intodevelopfrom
OBPIH-6395

Conversation

@kchelstowski
Copy link
Collaborator

Peek 2024-05-28 15-44

Copy link
Collaborator

Choose a reason for hiding this comment

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

Great work on tests!

const isCurrentStep = (iteratedStep) => iteratedStep.key === currentStepKey;
return (
<div className="steps-main-box">
<div className="steps-inside-wrapper">
Copy link
Collaborator

Choose a reason for hiding this comment

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

this className sounds slightly different to me: Can it be just "steps" or "steps-container"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Those are the class names used in the current WizardSteps.jsx. I think we want to keep the same styling for now, so the only difference in this component and the current WizardSteps is that I eventually call the step.title at the end, not the step. I didn't want to do some workarounds in the existing WizardSteps, this is why I created a brand new compoment, as it will be more maintainable if we decide to change anything there and not break the existing one.

};

const next = () => {
const nextStepIdx = steps.findIndex((s) => s.key === key) + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe it would be better to store the index of the key instead of recalculating the ID here and in the next 2 following functions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To be honest I was doing that, but when I saw the complexity (or rather the readability) after such refactor, I decided that it would be better to do it this way, because otherwise, I would have to return both: step and index from the const Step, and imho this would be ugly to access the step then via, e.g. Step.step, if in the useMemo we returned:

return {
  step: foundStep,
  currentStepIdx: idx 
}

if you guys strongly feel it's worth, then I can do that again.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I also think I would prefer Alan's suggestion.

@kchelstowski kchelstowski added the warn: do not merge Marks a pull request that is not yet ready to be merged label May 29, 2024
@kchelstowski kchelstowski removed the warn: do not merge Marks a pull request that is not yet ready to be merged label May 29, 2024
Comment on lines 7 to 52
describe('Changing steps', () => {
const steps = [
{
key: 1,
Component: () => (<div>First compoment</div>),
title: 'First',
},
{
key: 2,
Component: () => (<div>Second compoment</div>),
title: 'Second',
},
{
key: 3,
Component: () => (<div>Third compoment</div>),
title: 'Third',
},
{
key: 4,
Component: () => (<div>Fourth compoment</div>),
title: 'Fourth',
},
];

it('wizard will start with the initial key', () => {
const { result } = renderHook(() => useWizard({ initialKey: 3, steps }));

expect(result.current[0].key).toEqual(3);
});

it('wizard goes to next step', () => {
const { result } = renderHook(() => useWizard({ initialKey: 2, steps }));

act(() => {
result.current[1].next();
});

expect(result.current[0].key).toEqual(3);
});

it('wizard goes to previous step', () => {
const { result } = renderHook(() => useWizard({ initialKey: 4, steps }));

act(() => {
result.current[1].previous();
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

one nitpicky thing - the naming of tests (look into this comment) #3701 (comment)

Copy link
Collaborator Author

@kchelstowski kchelstowski May 29, 2024

Choose a reason for hiding this comment

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

Where is that convention documented? I can't find any resource opting for the "should" convention.

Copy link
Collaborator

Choose a reason for hiding this comment

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

do we have any conventions documented? 😆 😆 😆 😆

Copy link
Collaborator

Choose a reason for hiding this comment

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

When Alan was implementing tests a while ago, this was a request from me, "It should..." is a very common pattern for test name that explains the behavior and expected result like
"should return initial key"
"should advance to the next step when next is called"
"should go to the previous step when previous is called'"
"should go to the specific step when set is called"

}));

const [
Step,
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 get Component and key by destructuring?
I would also like to make the object returned from the hook flat. It doesn't make sense to add an additional object.

return [
    Step,
      set,
      first,
      next,
      previous,
      last,
  ];

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

imho it's cleaner to distinguish the step and the util hook functions, this is why I made it like this.

defaultLabel: "Stock Movement",
menuItems: [
[label: "outbound.create.label", defaultLabel: "Create Outbound Movements", href: "/stockMovement/createOutbound?direction=OUTBOUND"],
[label: "outbound.fullOutbound.import.label", defaultLabel: "Import Completed Outbound", href: "/stockMovement/fullOutboundImport"],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps it could be a simple: outbound.import.label?

render(view: "/common/react", params: params)
}

def fullOutboundImport() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have to have a "full" keyword everywhere?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I was going to say we really should keep things simple. Try not to use PIH terminology if there's a way to make it simple. Imagine some old Greek guys having a conversation and then distill their response.

Socrates: What does it do?
Kacperian: It provides a way to import a stock movement including all of its dependent objects like requests, picklists, etc.

importStockMovement()

Copy link
Member

Choose a reason for hiding this comment

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

or

importOutboundStockMovement()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would go with the 2nd option, especially if we are supposed to distinguish the SMs in the future for inbound, outbound etc.


import 'components/wizard/WizardSteps.scss';

const WizardStepsV2 = ({ steps, currentStepKey }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What has changed here compared to WizardSteps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

import useWizard from 'hooks/useWizard';

const FullOutboundImport = () => {
const FullOutboundImportStep = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

FullOutboundImportSteps

title: 'Confirm',
},
};
const steps = useMemo(() => [
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels a bit weird. Can we just have an object with full steps structure that would be transformed to list of values? Feels like this part could be simplified

};

const next = () => {
const nextStepIdx = steps.findIndex((s) => s.key === key) + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I also think I would prefer Alan's suggestion.

<div
key={step.key}
className={`step-container ${isCurrentStep(step) ? 'active' : ''}`}
data-testid={isCurrentStep(step) ? 'active' : 'inactive'}
Copy link
Collaborator

Choose a reason for hiding this comment

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

data-testid should not change conditionally I believe this should data-testid="wizzard-step" and if you need to specify the state of the step we should use a different attribute like a custom
data-stepstate={isCurrentStep(step) ? 'active' : 'inactive'}
or
aria-label={isCurrentStep(step) ? 'active' : 'inactive'}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was copied from the existing WizardSteps, so I did not even realize and thought about it, but since you are testing ninja, I trust you 😆

Comment on lines 8 to 10
const set = (val) => {
setKey(val);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

this looks unnecessary we can just return the setKey function in the hook like

return {
 set: setKey
}

Comment on lines +12 to +19
const first = () => {
setKey(steps[0]?.key);
};

const last = () => {
const lastIdx = steps.length - 1;
setKey(steps[lastIdx]?.key);
};
Copy link
Collaborator

@drodzewicz drodzewicz May 29, 2024

Choose a reason for hiding this comment

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

It's cool that you added these but are we ever going to use them?
I don't care if they stay or they go, I am just not familiar with any use case where we will want to jump to first and last steps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We already have such use cases - e.g. if we have an SM in progress and it is supposed to be on "X" step, we go to step "X".

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes, but I am not talking about function set but last and first

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

maybe now we don't have the use case, but they felt cute and I also wanted to have a complete hook, that would cover any of the future cases, so I kept them :sadsmile:
Let me know if I should remove them in your opinion guys.

Comment on lines 7 to 52
describe('Changing steps', () => {
const steps = [
{
key: 1,
Component: () => (<div>First compoment</div>),
title: 'First',
},
{
key: 2,
Component: () => (<div>Second compoment</div>),
title: 'Second',
},
{
key: 3,
Component: () => (<div>Third compoment</div>),
title: 'Third',
},
{
key: 4,
Component: () => (<div>Fourth compoment</div>),
title: 'Fourth',
},
];

it('wizard will start with the initial key', () => {
const { result } = renderHook(() => useWizard({ initialKey: 3, steps }));

expect(result.current[0].key).toEqual(3);
});

it('wizard goes to next step', () => {
const { result } = renderHook(() => useWizard({ initialKey: 2, steps }));

act(() => {
result.current[1].next();
});

expect(result.current[0].key).toEqual(3);
});

it('wizard goes to previous step', () => {
const { result } = renderHook(() => useWizard({ initialKey: 4, steps }));

act(() => {
result.current[1].previous();
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

When Alan was implementing tests a while ago, this was a request from me, "It should..." is a very common pattern for test name that explains the behavior and expected result like
"should return initial key"
"should advance to the next step when next is called"
"should go to the previous step when previous is called'"
"should go to the specific step when set is called"

Copy link
Member

@jmiranda jmiranda left a comment

Choose a reason for hiding this comment

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

@kchelstowski Wait does this mean I can finally click on the wizard steps to go through the flow? I'm so confused. And excited.

@kchelstowski
Copy link
Collaborator Author

@jmiranda yes, I've just done the "background" for such behavior, but to be honest we've already had such a behavior, but it was kinda "freezed" (it's been prepared but it has been disabled throughout all of the current wizards)

@awalkowiak
Copy link
Collaborator

@jmiranda clicking on a step is supported, but disabled/not used on "old" wizards.

@awalkowiak awalkowiak merged commit 8f81281 into develop Jun 3, 2024
@awalkowiak awalkowiak deleted the OBPIH-6395 branch June 3, 2024 14:02
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.

5 participants