Skip to content

Commit

Permalink
Update KAIO version of the FMS to reflect recent fix to accessible ti…
Browse files Browse the repository at this point in the history
…tles (#3996)

* add changes from the FMS legacy to the KAIO verison

* clean up story

* run linter and add changes
  • Loading branch information
mcwinter07 authored Aug 22, 2023
1 parent 4577248 commit d91bfe0
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 74 deletions.
12 changes: 12 additions & 0 deletions .changeset/lemon-phones-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@kaizen/components": minor
---

Update FilterMultiSelect ListBoxSection to avoid duplicate reading of sectionName as the accessible title.

- Update sectionName to be optional if sectionHeader is provided
- This will solve the issue of sectionName and sectionHeader being read twice when they are the same
- Minor style change to ensure hide bullet lists as filtering
- Minor style changes to allow for default text styles for section headings with just text
- Add conditional check to render the sectionName only if provided
- Add tests to validate type accessible names are constructed as expected
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const WithSections: StoryFn<typeof FilterMultiSelect> = () => {
selectedKeys={selectedKeys}
items={mockItems}
label="Engineer"
isOpen={IS_CHROMATIC || undefined}
trigger={(): JSX.Element => (
<FilterMultiSelect.TriggerButton
selectedOptionLabels={getSelectedOptionLabels(
Expand Down Expand Up @@ -222,13 +223,14 @@ export const WithSections: StoryFn<typeof FilterMultiSelect> = () => {
</FilterMultiSelect>
<div style={{ marginTop: 4 }}>
<Paragraph variant="body">Items: </Paragraph>
<Highlight className="json">
{JSON.stringify(mockItems, null, "\t")}
</Highlight>
<Highlight>{JSON.stringify(mockItems, null, "\t")}</Highlight>
</div>
</>
)
}
WithSections.parameters = {
chromatic: { disable: false },
}

export const TruncatedLabels: StoryFn<typeof FilterMultiSelect> = () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(
Expand Down Expand Up @@ -606,7 +608,7 @@ export const Async: StoryFn<typeof FilterMultiSelect> = args => {
}
Async.decorators = [withQueryProvider]

export const WithSectionHeader: StoryFn<typeof FilterMultiSelect> = () => {
export const WithSectionHeaders: StoryFn<typeof FilterMultiSelect> = () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(
new Set(["id-fe"])
)
Expand All @@ -620,6 +622,7 @@ export const WithSectionHeader: StoryFn<typeof FilterMultiSelect> = () => {
selectedKeys={selectedKeys}
items={mockItems}
label="Engineer"
isOpen={IS_CHROMATIC || undefined}
trigger={(): JSX.Element => (
<FilterMultiSelect.TriggerButton
selectedOptionLabels={getSelectedOptionLabels(
Expand All @@ -639,63 +642,176 @@ export const WithSectionHeader: StoryFn<typeof FilterMultiSelect> = () => {
unselectedItems,
disabledItems,
hasNoItems,
}): JSX.Element => (
<>
{hasNoItems && (
<FilterMultiSelect.NoResults>
No results found.
</FilterMultiSelect.NoResults>
)}
<FilterMultiSelect.ListBoxSection
items={selectedItems}
sectionName="Selected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option key={item.key} item={item} />
}): JSX.Element =>
hasNoItems ? (
<FilterMultiSelect.NoResults>
No results found.
</FilterMultiSelect.NoResults>
) : (
<>
{selectedItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={selectedItems}
sectionHeader="Selected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}
</FilterMultiSelect.ListBoxSection>

{unselectedItems.length > 0 && selectedItems.length > 0 && (
<FilterMultiSelect.SectionDivider />
)}

<FilterMultiSelect.ListBoxSection
items={unselectedItems}
sectionName="Unselected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option key={item.key} item={item} />
{unselectedItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={unselectedItems}
sectionHeader="Unselected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}
</FilterMultiSelect.ListBoxSection>

{disabledItems.length > 0 &&
(selectedItems.length > 0 ||
unselectedItems.length > 0) && (
<FilterMultiSelect.SectionDivider />
)}
<FilterMultiSelect.ListBoxSection
items={disabledItems}
sectionName="Disabled items"
sectionHeader={
<InlineNotification
type="cautionary"
persistent
noBottomMargin
headingProps={{
tag: "span",
variant: "heading-5",
children: "Confidentiality protection",
}}
{disabledItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={disabledItems}
sectionHeader="Disabled items"
>
Results for these filters are hidden to protect
identities of individuals and small groups
</InlineNotification>
}
>
{(item): JSX.Element => (
<FilterMultiSelect.Option key={item.key} item={item} />
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}
</FilterMultiSelect.ListBoxSection>
</>
)
}
</FilterMultiSelect.ListBox>
<FilterMultiSelect.MenuFooter>
<FilterMultiSelect.SelectAllButton />
<FilterMultiSelect.ClearButton />
</FilterMultiSelect.MenuFooter>
</>
)}
</FilterMultiSelect>
</>
)
}
WithSectionHeaders.parameters = {
chromatic: { disable: false },
}

export const WithSectionNotification: StoryFn<
typeof FilterMultiSelect
> = () => {
const [selectedKeys, setSelectedKeys] = useState<Selection>(
new Set(["id-fe"])
)

const handleSelectionChange = (keys: Selection): void => setSelectedKeys(keys)

return (
<>
<FilterMultiSelect
onSelectionChange={handleSelectionChange}
selectedKeys={selectedKeys}
items={mockItems}
label="Engineer"
isOpen={IS_CHROMATIC || undefined}
trigger={(): JSX.Element => (
<FilterMultiSelect.TriggerButton
selectedOptionLabels={getSelectedOptionLabels(
selectedKeys,
mockItems
)}
label="Engineer"
/>
)}
>
{(): JSX.Element => (
<>
<FilterMultiSelect.SearchInput />
<FilterMultiSelect.ListBox>
{({
selectedItems,
unselectedItems,
disabledItems,
hasNoItems,
}): JSX.Element => (
<>
{hasNoItems ? (
<FilterMultiSelect.NoResults>
No results found.
</FilterMultiSelect.NoResults>
) : (
<>
{selectedItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={selectedItems}
sectionHeader="Selected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}

{unselectedItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={unselectedItems}
sectionHeader="Unselected items"
>
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}

{disabledItems.length > 0 && (
<FilterMultiSelect.ListBoxSection
items={disabledItems}
sectionHeader={
<>
<span className="mb-6">Disabled items</span>
<InlineNotification
type="cautionary"
persistent
noBottomMargin
headingProps={{
tag: "span",
variant: "heading-5",
children: "Confidentiality protection",
}}
>
Results for these filters are hidden to protect
identities of individuals and small groups
</InlineNotification>
</>
}
>
{(item): JSX.Element => (
<FilterMultiSelect.Option
key={item.key}
item={item}
/>
)}
</FilterMultiSelect.ListBoxSection>
)}
</>
)}
</>
)}
</FilterMultiSelect.ListBox>
Expand All @@ -706,12 +822,9 @@ export const WithSectionHeader: StoryFn<typeof FilterMultiSelect> = () => {
</>
)}
</FilterMultiSelect>
<div style={{ marginTop: 4 }}>
<Paragraph variant="body">Items: </Paragraph>
<Highlight className="json">
{JSON.stringify(mockItems, null, "\t")}
</Highlight>
</div>
</>
)
}
WithSectionNotification.parameters = {
chromatic: { disable: false },
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@
.hidden {
display: none;
}

// this is a div but remove styles briefly flickering to a bullet list as the sections are removed
.noResultsWrapper {
list-style: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const ListBox = ({ children }: ListBoxProps): JSX.Element => {
if (hasNoItems) {
return (
<>
<div>{children(itemsState)}</div>
<div className={styles.noResultsWrapper}>{children(itemsState)}</div>
{/* This ul with the ref needs to exist otherwise it fatals */}
<ul ref={listRef} className={styles.hidden} />
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
@import "~@kaizen/design-tokens/sass/typography";
@import "~@kaizen/design-tokens/sass/color";
@import "~@kaizen/design-tokens/sass/spacing";

.listBoxSection {
display: grid;
list-style: none;
padding: 0;
}

.listBoxSectionHeader {
font-family: $typography-heading-6-font-family;
font-size: $typography-heading-6-font-size;
font-weight: $typography-heading-6-font-weight;
line-height: $typography-heading-6-line-height;
color: rgba($color-purple-800-rgb, 0.7);
margin: $spacing-6 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react"
import { render } from "@testing-library/react"
import { ListBoxSection } from "./ListBoxSection"

describe("<ListBoxSection />", () => {
describe("sectionName only", () => {
it("will only have aria-label", () => {
const { getByRole } = render(
<ListBoxSection sectionName="Test sectionName only" items={[]}>
{() => undefined}
</ListBoxSection>
)
const group = getByRole("group")
expect(group).toHaveAttribute("aria-label", "Test sectionName only")
expect(group).not.toHaveTextContent("Test sectionName only")
})
})

describe("sectionHeader only", () => {
it("will have sectionHeader content", () => {
const { getByRole } = render(
<ListBoxSection sectionHeader="Test sectionHeader only" items={[]}>
{() => undefined}
</ListBoxSection>
)
const group = getByRole("group", { name: "Test sectionHeader only" })
expect(group).toBeInTheDocument()
expect(group).toHaveTextContent("Test sectionHeader only")
})
})

describe("sectionHeader and sectionName", () => {
it("will have combined accessible name", () => {
const { getByRole } = render(
<ListBoxSection
sectionName="Hidden group name"
sectionHeader="sectionHeader name"
items={[]}
>
{() => undefined}
</ListBoxSection>
)
const group = getByRole("group", {
name: "Hidden group name. sectionHeader name",
})
expect(group).toBeInTheDocument()
expect(group).toHaveTextContent("Hidden group name. sectionHeader name")
})
})
})
Loading

0 comments on commit d91bfe0

Please sign in to comment.