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(step-flow): add new component #6481

Merged
merged 1 commit into from
Feb 6, 2024
Merged

feat(step-flow): add new component #6481

merged 1 commit into from
Feb 6, 2024

Conversation

tomdavies73
Copy link
Contributor

@tomdavies73 tomdavies73 commented Dec 8, 2023

Proposed behaviour

Screenshot 2023-12-08 at 16 27 29

This PR will add the StepFlow component which aims to help users advance through a user journey, and keep track of their progress. This is done initially with a very simple default configuration, with just the current title, current step and the total steps in the journey. This can also be contextualised further with an optional category prop.

Screenshot 2023-12-08 at 16 27 40

Screenshot 2023-12-08 at 16 33 37

However, a progress indicator can also be rendered via the showProgressIndicator prop, providing an added decorative indicator of progress, with indicators holding three individual states: completed, in progress, not completed ; each with their own background colours.

A close icon can also be rendered, with its own onDismiss prop, allowing consumers to pass functions to be called onClick such as closing a modal.

Screenshot 2023-12-08 at 16 27 47

The component is also flexible, allowing for multiple steps to be set with the currentStep and totalSteps props, both can be between 1-8. Also currentStep can never exceed the value of totalSteps. When these are changed, the label underneath the title will change, as well as the indicator bar (if rendered).

The component also has built-in accessibility considerations with a focus ref being provided which provides consumers with the ability to pull focus to a visually hidden DOM element containing a formatted string with all of the relevant titles, category, step progress contained within.

This ensures screen reader users are given all the relevant information through semantic HTML on first navigation, and then when they progress through the journey, using buttons, for example focus is then pulled to the DOM element shown above. This will announce all of the relevant text content and then allow them to navigate down the page through all the relevant content below.

Current behaviour

Currently there is no accessible component which provides means to track and display a step journey or sequence.

Checklist

  • Commits follow our style guide
  • Related issues linked in commit messages if required
  • Screenshots are included in the PR if useful
  • All themes are supported if required
  • Unit tests added or updated if required
  • Cypress automation tests added or updated if required
  • Playwright automation tests added or updated if required
  • Storybook added or updated if required
  • Translations added or updated (including creating or amending translation keys table in storybook) if required
  • Typescript d.ts file added or updated if required
  • Related docs have been updated if required

QA

  • Tested in CodeSandbox/storybook
  • Add new Cypress test coverage if required
  • Carbon implementation matches Design System/designs
  • UI Tests GitHub check reviewed if required

Additional context

Testing instructions

The following CodeSandbox is an example of the broken behaviour.
You can see the new behaviour by looking at the version in the comment by codesandbox[bot].

Copy link

codesandbox-ci bot commented Dec 8, 2023

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 810ecb2:

Sandbox Source
carbon-quickstart Configuration
carbon-quickstart-typescript Configuration
carbon-quickstart PR

@edleeks87 edleeks87 self-requested a review December 12, 2023 15:41
Copy link
Contributor

@edleeks87 edleeks87 left a comment

Choose a reason for hiding this comment

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

Great effort @tomdavies73, especially on the considerations you've given to accessibility. I've left some comments but they're fairly straightforward to address, on the whole really good work 👏

flex-direction: column;
`;

const StyledCategoryText = styled(Typography)`
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment: we can probably just use Typography here directly as it already supports the interface for all this styling


const StyledTitleTextContainer = styled.span``;

const StyledVisibleTitleText = styled.span`
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment: if you simplify the HTML like Ive suggested above you can probably just pass these to Typography directly


const StyledProgressIndicator = styled.span<StyledProgressIndicatorProps>`
background-color: ${({ isCompleted, isInProgress }) =>
calculateProgressIndicatorColor({ isCompleted, isInProgress })};
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (non-blocking): you don't need to destructure here props are passed implicitly so you could just do:

background-color: ${calculateProgressIndicatorColor};

background-color: ${({ isCompleted, isInProgress }) =>
calculateProgressIndicatorColor({ isCompleted, isInProgress })};
width: 100%;
height: 8px;
Copy link
Contributor

Choose a reason for hiding this comment

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

comment: wherever possible we should use tokens such as --spacing150, --borderRadius100 etc.

Comment relates to whole file (font sizes etc)

/** Set the variant of the internal 'Typography' component which contains the category.
* However, despite the chosen variant the styling will always be overridden.
*/
categoryTypographyVariant?: VariantTypes;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
categoryTypographyVariant?: VariantTypes;
categoryVariant? :Extract<VariantTypes, "h1" | "h2" | "h3" | "h4" | "h5" | "p">;

Naming is non-blocking for me but I think we should limit the elements here as some don't seem viable for this use case

return (
<StyledStepFlow {...rest} {...tagComponent("step-flow", rest)}>
<StyledStepContent>
<StyledStepContentText>
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment: this would render an empty container if no category is set. We could probably get away with using Typography directly here is we add support for aria-hidden etc

);
});

const closeIcon = (
Copy link
Contributor

Choose a reason for hiding this comment

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

comment: lets add an aria-label here and support translations on it etc

await checkAccessibility(page);
});

test("should pass accessibility checks when 'showCloseIcon' is true", async ({
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: maybe worth focusing the close icon and then checking against axe here

);

expect(
container.querySelector('[data-element="close"]')
Copy link
Contributor

Choose a reason for hiding this comment

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

comment: if we set and aria-label (see above) we can get by label here instead of using a container to query.


<StyledSystemProps of={StepFlow} margin noHeader />

<TranslationKeysTable
Copy link
Contributor

Choose a reason for hiding this comment

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

comment: don't forget to update this with the close aria label

@tomdavies73 tomdavies73 force-pushed the FE-6278 branch 3 times, most recently from c896c6d to dfa7725 Compare January 9, 2024 14:11
Copy link
Contributor

@DipperTheDan DipperTheDan left a comment

Choose a reason for hiding this comment

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

Excellent work on this, @tomdavies73. Just a few comments from me, nothing serious though 😄👍🏻


const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS];

test.describe("Prop checks for Portrait component", () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

typo:

Suggested change
test.describe("Prop checks for Portrait component", () => {
test.describe("Prop checks for Step Flow 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.

Genuinely do not know how i missed this

<StepFlowComponent currentStep={1} totalSteps={1} showProgressIndicator />
);

const backgroundColorToken = await getDesignTokensByCssProperty(
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion(non-blocking): You may not need this await here.

Suggested change
const backgroundColorToken = await getDesignTokensByCssProperty(
const backgroundColorToken = getDesignTokensByCssProperty(

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 think we do need an await here as its a promise, without it backgroundColorToken returns undefined

<StepFlowComponent currentStep={2} totalSteps={3} showProgressIndicator />
);

const backgroundColorToken = await getDesignTokensByCssProperty(
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion(non-blocking): Same again. You may not need this await here.

Suggested change
const backgroundColorToken = await getDesignTokensByCssProperty(
const backgroundColorToken = getDesignTokensByCssProperty(

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 think we do need an await here as its a promise, without it backgroundColorToken returns undefined

<StepFlowComponent currentStep={2} totalSteps={3} showProgressIndicator />
);

const backgroundColorToken = await getDesignTokensByCssProperty(
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion(non-blocking): Same again. You may not need this await here.

Suggested change
const backgroundColorToken = await getDesignTokensByCssProperty(
const backgroundColorToken = getDesignTokensByCssProperty(

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 think we do need an await here as its a promise, without it backgroundColorToken returns undefined

Comment on lines 209 to 224
await stepFlowDismissIcon(page).click();
expect(callbackCount).toBe(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick(non-blocking): space here otherwise the QA's will break your legs.

Suggested change
await stepFlowDismissIcon(page).click();
expect(callbackCount).toBe(1);
await stepFlowDismissIcon(page).click();
expect(callbackCount).toBe(1);

Comment on lines 222 to 238
await button(page).nth(1).click();
await expect(stepFlowTitleTextWrapper(page)).toBeFocused();
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick(non-blocking): Same again with the broken legs.

Suggested change
await button(page).nth(1).click();
await expect(stepFlowTitleTextWrapper(page)).toBeFocused();
await button(page).nth(1).click();
await expect(stepFlowTitleTextWrapper(page)).toBeFocused();

await checkAccessibility(page);
});

test("should pass accessibility checks when component is rendered with a ref", async ({
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion(non-blocking): Just because the example is called StepFlowComponentWithRefAndButtons

Suggested change
test("should pass accessibility checks when component is rendered with a ref", async ({
test("should pass accessibility checks when component is rendered with a ref and buttons", async ({

Copy link
Contributor

@edleeks87 edleeks87 left a comment

Choose a reason for hiding this comment

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

Real good updates @tomdavies73, especially how thorough you've been in adding additional testing 👍


it("when the 'titleVariant' prop is not passed, the variant is h1 by default", () => {
const { container } = render(
<StepFlow title="this title is a h1" currentStep={5} totalSteps={6} />
Copy link
Contributor

Choose a reason for hiding this comment

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

comment (non-blocking, don't actually change anything): I suppose depending if you say aitch or haitch could affect whether it's a h1 or an h1 here 😆

const user = userEvent.setup();
const { container } = render(<MockComponent />);
const button = screen.getByRole("button");
button.focus();
Copy link
Contributor

Choose a reason for hiding this comment

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

comment: I don't think you need to focus the button here (just click) and given the what this is testing it may be a little confusing

@@ -39,9 +40,9 @@ import StepFlow from "carbon-react/lib/components/step-flow";

## Examples

The `titleRef` prop is mandatory, and must be passed when the component is used to ensure focus is pulled to the components title whenever
A `ref` is **strongly** recommended to be used, to ensure focus is programmatically moved to a a title div when
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion (non-blocking):

To ensure focus is programmatically moved to the `title` of a given `StepFlow` when a user advances through each step,  pass a `ref` is  **strongly** recommended. This ensures screen reader users are made aware of all information throughout the user journey

edleeks87
edleeks87 previously approved these changes Jan 10, 2024
DipperTheDan
DipperTheDan previously approved these changes Jan 10, 2024
@harpalsingh
Copy link
Member

We (myself and @tempertemper )are happy with the suggested accessibility solution, however request from a semantic pov that the current h1/h2/h3 spec is changed to span/h1/span, and the ability to change the h1 semantically to h2 if required. Visually nothing will change from the design.

@harpalsingh
Copy link
Member

As discussed with @tempertemper changes made so only the title is h1 by default but can be changed to a h2 and the Category and steps are now just spans.

stephenogorman
stephenogorman previously approved these changes Feb 6, 2024
creates a new carbon component to aid consumers in creating and offering means
for end users to advance through a user journey. The component also has
built-in accessibility considerations with a custom ref handle being provided which
provides consumers with the ability to programmatically move focus to a visually hidden
DOM element which consists of a formatted string with the relevant title, category,
and step progress
@edleeks87 edleeks87 merged commit f25e78f into master Feb 6, 2024
22 checks passed
@edleeks87 edleeks87 deleted the FE-6278 branch February 6, 2024 16:49
@carbonci
Copy link
Collaborator

carbonci commented Feb 6, 2024

🎉 This PR is included in version 125.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

6 participants