Skip to content

Commit

Permalink
code review feedback
Browse files Browse the repository at this point in the history
- test the DOM element exists in RenderElement tests
- move the CustomLinkComponent out of the example, to avoid folks copying it
- render element docs page broken by linter, props merging section now appears
  • Loading branch information
mark-tate committed Jun 28, 2024
1 parent 422a685 commit 85ed339
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .changeset/tender-avocados-develop.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"@salt-ds/core": minor
---

add `render` prop to `NavigationItem`
Added `render` prop to `NavigationItem`

- the `render` prop enables the substitution of the default anchor tag with an alternate link, such as React Router, facilitating integration with routing libraries.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NavigationItem, NavigationItemRenderProps } from "@salt-ds/core";
import { NavigationItem } from "@salt-ds/core";
import { NotificationIcon } from "@salt-ds/icons";

describe("GIVEN a NavItem", () => {
Expand Down Expand Up @@ -103,7 +103,10 @@ describe("GIVEN a NavItem", () => {

describe("AND `render` is passed a render function", () => {
it("should call `render` to create parent item", () => {
const mockRender = cy.stub().as("render");
const mockRender = cy
.stub()
.as("render")
.returns(<button>Parent Button</button>);
cy.mount(
<NavigationItem
active={true}
Expand All @@ -117,6 +120,7 @@ describe("GIVEN a NavItem", () => {
Navigation Item
</NavigationItem>
);
cy.findByText("Parent Button").should("exist");
cy.get("@render").should("have.been.calledWithMatch", {
parent: true,
active: true,
Expand All @@ -133,7 +137,10 @@ describe("GIVEN a NavItem", () => {
});
});
it("should call `render` to create child item", () => {
const mockRender = cy.stub().as("render");
const mockRender = cy
.stub()
.as("render")
.returns(<a>Navigation Link</a>);
cy.mount(
<NavigationItem
active={true}
Expand All @@ -147,6 +154,7 @@ describe("GIVEN a NavItem", () => {
Navigation Item
</NavigationItem>
);
cy.findByText("Navigation Link").should("exist");
cy.get("@render").should("have.been.calledWithMatch", {
parent: false,
active: true,
Expand All @@ -168,10 +176,7 @@ describe("GIVEN a NavItem", () => {
describe("AND `render` is given a JSX element", () => {
it("should merge the props and render the JSX element ", () => {
cy.mount(
<NavigationItem
parent={true}
render={<button id={"button"}>Button Children</button>}
>
<NavigationItem parent={true} render={<button>Button Children</button>}>
Navigation Item
</NavigationItem>
);
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/navigation-item/ExpansionIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const iconExpansionMap = {
},
};

type ExpansionIconProps = {
interface ExpansionIconProps {
/**
* Whether the navigation item is expanded.
*/
Expand All @@ -25,7 +25,7 @@ type ExpansionIconProps = {
* The orientation of the navigation item.
*/
orientation?: "horizontal" | "vertical";
};
}

export const ExpansionIcon: FC<ExpansionIconProps> = ({
expanded = false,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/navigation-item/NavigationItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface NavigationItemRenderProps
"active" | "expanded" | "level" | "parent" | "orientation"
> {
/**
* Props to apply to the chld row to render a link
* Props to apply to the child row to render a link
*/
linkProps?: AnchorHTMLAttributes<HTMLAnchorElement>;
/**
Expand Down Expand Up @@ -128,7 +128,7 @@ export const NavigationItem = forwardRef<HTMLDivElement, NavigationItemProps>(
"--saltNavigationItem-level": `${level}`,
};

const isParent = parent || href === undefined; // for backwards compatiblity with original
const isParent = parent || href === undefined;
const elementProps = {
className: clsx(
withBaseName("wrapper"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,13 +589,14 @@ export const VerticalNestedGroupNoIcon = () => {
);
};

const CustomLinkImplementation = (props: NavigationItemRenderProps) => (
<a {...props} aria-label={"overridden-label"}>
<Text>Your Own Link Implementation</Text>
</a>
);

export const WithRenderElement = () => {
const [expanded, setExpanded] = useState<boolean>(false);
const CustomLinkImplementation = (props: NavigationItemRenderProps) => (
<a {...props} aria-label={"overridden-label"}>
<Text>Your Own Link Implementation</Text>
</a>
);
return (
<nav>
<StackLayout
Expand Down
28 changes: 13 additions & 15 deletions site/docs/components/navigation-item/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,37 +96,35 @@ When the user collapses a vertical nested group, and there is an active nested i
<LivePreview componentName="navigation-item" exampleName="RenderElement">
## Render prop element

Using the `render` prop element, you can customise the tag rendered by your `NavigationItem`.
Using the `render` prop, you can customize the element rendered by the `NavigationItem`.
Props defined on the JSX element will be merged with props from the `NavigationItem`.

A `NavigationItem` will either act as a navigational link, or an expandable group for other `NavigationItem` elements.
A `NavigationItem` will either act as a link, or an expandable group for other `NavigationItem` elements.
When a parent group is clicked, the group will expand or collapse.
When a link is clicked, navigates the user to the specified `href` URL.

The default element for parent groups is a HTML button tag, whilst a link will use a HTML anchor tag.
The default element for parent groups is a `<button>`, whilst a link will use a `<a>`.

### Prop merging

`<NavigationItem href="/your-page" render={<a target="_blank"/>} />`
```
<NavigationItem href="/your-page" render={<a target="_blank"/>} />
```

<br />
<br />
renders
<br />
<br />`
<a href="/your-page" target="_blank">
...
</a>
`
will be created in the DOM as an `<a/>` with merged props

```
<a href="/your-page" target="_blank">...</a>
```

</LivePreview>

<LivePreview componentName="navigation-item" exampleName="RenderProp">
## Render prop callback

Using the `render` prop element, you can customise the tag rendered by your `NavigationItem`.
Using the `render` prop, you can customize the element rendered by the `NavigationItem`.

The render prop can also accept a function. This approach grants enhanced control over how props are merged, allowing for more precise customization of component behavior.
The render prop can also accept a function. This approach allows more control over how props are merged, allowing for more precise customization of the component's behavior.

When a function is passed to the render prop, it's your responsibility to merge props within the function itself.

Expand Down
11 changes: 6 additions & 5 deletions site/src/examples/navigation-item/RenderElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {
Text,
} from "@salt-ds/core";

const CustomLinkImplementation = (props: NavigationItemRenderProps) => (
<a {...props} aria-label={"overridden-label"}>
<Text>Your Own Link Implementation</Text>
</a>
);

export const RenderElement = (): ReactElement => {
const [expanded, setExpanded] = useState<boolean>(false);
const CustomLinkImplementation = (props: NavigationItemRenderProps) => (
<a {...props} aria-label={"overridden-label"}>
<Text>Your Own Link Implementation</Text>
</a>
);
return (
<nav>
<StackLayout
Expand Down

0 comments on commit 85ed339

Please sign in to comment.