Skip to content

Commit

Permalink
feat(breadcrumb): create new component (#163)
Browse files Browse the repository at this point in the history
Co-authored-by: felipefialho <[email protected]>
  • Loading branch information
denilsonrp and felipefialho authored Jul 19, 2023
1 parent 10e7b9a commit 4ce3b96
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Meta } from '@storybook/addon-docs'

<Meta title="Docs/Architecture Decision Records/ADR 0009: Why use onClick event on breadcrumb component even undocumented?" />

# Why use `onClick` event on breadcrumb component even undocumented?

🗓️ 2023-07 · ✍️ [@denilsonrp](https://github.com/denilsonrp)

## Context

In the [breadcrumb component's pull request](https://github.com/juntossomosmais/atomium/pull/163) a [doubt arose about the correct functioning of the `onClick` event](https://github.com/juntossomosmais/atomium/pull/163#discussion_r1250986555), as this event isn't documented in [ion-breadcrumb docs](https://ionicframework.com/docs/api/breadcrumb#events).

## Decision

We ran some tests on Atomium's breadcrumb component in order to ensure the correct firing of the event and even though the `onClick` event isn't documented in the events section of the [ion-breadcrumb docs](https://ionicframework.com/docs/api/breadcrumb#events), the tests were successful and the event fired correctly.

Based on these tests, we can assume that, even without event documentation, it's possible to use it. The idea of ​​exposing this decision is to serve as a reference in possible similar scenarios in the future.
2 changes: 1 addition & 1 deletion packages/core/src/components/alert/alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
}

.atom-alert {
border-radius: var(--border-radius);
border: 1px solid transparent;
border-radius: var(--border-radius);
font: var(--text-body-medium);
letter-spacing: var(--text-body-medium-letter);
padding: var(--spacing-base);
Expand Down
60 changes: 60 additions & 0 deletions packages/core/src/components/breadcrumb/breadcrumb.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@import '~@atomium/scss-utils/index';

.atom-breadcrumbs {
display: none;

@include above(medium) {
display: flex;
}
}

.atom-breadcrumb {
--color: var(--color-brand-secondary-regular);
--color-active: var(--color-neutral-regular);
font: var(--text-body-small);
letter-spacing: var(--text-body-small-letter);

&:not(:last-of-type) {
&::part(native) {
cursor: pointer;

&:hover {
text-decoration: underline;
}
}

.atom-breadcrumb__text {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

&::part(native) {
padding: 0;
}

&::part(separator) {
color: var(--color-neutral-regular);
margin: 0 var(--spacing-xxsmall);
}
}

.atom-button__back {
align-items: center;
background-color: transparent;
border: 0;
color: var(--color-brand-secondary-regular);
cursor: pointer;
display: flex;
font: var(--text-link-small);
gap: var(--spacing-xsmall);
letter-spacing: var(--text-link-small-letter);
padding: 0;
text-decoration: underline;

@include above(medium) {
display: none;
}
}
90 changes: 90 additions & 0 deletions packages/core/src/components/breadcrumb/breadcrumb.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { newSpecPage } from '@stencil/core/testing'

import { AtomBreadcrumb } from './breadcrumb'

const breadcrumbItemsMock = [
{
text: 'First level',
title: 'Go to first level',
redirect: () => console.log('/first'),
},
{
text: 'Intermediary level',
title: 'Go to intermediary level',
redirect: () => console.log('/intermediary'),
},
{
text: 'Current level',
title: 'This is the current level',
},
]

describe('atom-breadcrumb', () => {
it('should render breadcrumb items and mobile back button correctly', async () => {
const page = await newSpecPage({
components: [AtomBreadcrumb],
html: `<atom-breadcrumb />`,
})

page.rootInstance.items = breadcrumbItemsMock

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-breadcrumb>
<mock:shadow-root>
<button class="atom-button__back" type="button">
<atom-icon icon="west" size="small"></atom-icon>
Voltar para Intermediary level
</button>
<ion-breadcrumbs class="atom-breadcrumbs">
<ion-breadcrumb class="atom-breadcrumb">
<span class="atom-breadcrumb__text" title="Go to first level">
First level
</span>
<atom-icon icon="arrow-right" slot="separator"></atom-icon>
</ion-breadcrumb>
<ion-breadcrumb class="atom-breadcrumb">
<span class="atom-breadcrumb__text" title="Go to intermediary level">
Intermediary level
</span>
<atom-icon icon="arrow-right" slot="separator"></atom-icon>
</ion-breadcrumb>
<ion-breadcrumb class="atom-breadcrumb">
<span class="atom-breadcrumb__text" title="This is the current level">
Current level
</span>
<atom-icon icon="arrow-right" slot="separator"></atom-icon>
</ion-breadcrumb>
</ion-breadcrumbs>
</mock:shadow-root>
</atom-breadcrumb>
`)
})

it('should not render mobile back button when only one breadcrumb item is sent as prop', async () => {
const page = await newSpecPage({
components: [AtomBreadcrumb],
html: `<atom-breadcrumb />`,
})

page.rootInstance.items = [breadcrumbItemsMock[0]]

await page.waitForChanges()

expect(page.root).toEqualHtml(`
<atom-breadcrumb>
<mock:shadow-root>
<ion-breadcrumbs class="atom-breadcrumbs">
<ion-breadcrumb class="atom-breadcrumb">
<span class="atom-breadcrumb__text" title="Go to first level">
First level
</span>
<atom-icon icon="arrow-right" slot="separator"></atom-icon>
</ion-breadcrumb>
</ion-breadcrumbs>
</mock:shadow-root>
</atom-breadcrumb>
`)
})
})
50 changes: 50 additions & 0 deletions packages/core/src/components/breadcrumb/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Component, h, Host, Prop } from '@stencil/core'

type BreadcrumbItemProp = {
redirect?: (event: MouseEvent) => void
text: string
title: string
}

@Component({
tag: 'atom-breadcrumb',
styleUrl: 'breadcrumb.scss',
shadow: true,
})
export class AtomBreadcrumb {
@Prop({ mutable: true }) items: BreadcrumbItemProp[] = []

render() {
const prevItem = this.items[this.items.length - 2]

return (
<Host>
{this.items.length > 1 && (
<button
class="atom-button__back"
type="button"
onClick={prevItem.redirect}
>
<atom-icon icon="west" size="small" />
Voltar para {prevItem.text}
</button>
)}

<ion-breadcrumbs class="atom-breadcrumbs">
{this.items.map((item) => (
<ion-breadcrumb
class="atom-breadcrumb"
onClick={item.redirect}
key={item.text}
>
<span class="atom-breadcrumb__text" title={item.title}>
{item.text}
</span>
<atom-icon icon="arrow-right" slot="separator" />
</ion-breadcrumb>
))}
</ion-breadcrumbs>
</Host>
)
}
}
25 changes: 25 additions & 0 deletions packages/core/src/components/breadcrumb/stories/breadcrumb.args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Category } from '@atomium/storybook-utils/enums/table'

export const BreadcrumbStoryArgs = {
decorators: [],
parameters: {
actions: {
handles: [],
},
docs: {
description: {
component:
'Breadcrumbs are navigation items that are used to indicate where a user is on site. Read the [Ionic documentation](https://ionicframework.com/docs/api/breadcrumbs) for more information about this component.',
},
},
},
argTypes: {
items: {
description:
'This is the list of items that will be displayed in the breadcrumb. Items must have `title` and `text` and optionally a `redirect` function when clicking on the item.',
table: {
category: Category.PROPERTIES,
},
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Meta, StoryObj } from '@storybook/web-components'

import { html } from 'lit'

import { BreadcrumbStoryArgs } from './breadcrumb.args'

export default {
title: 'Components/Breadcrumb',
...BreadcrumbStoryArgs,
} as Meta

const createBreadcrumb = () => {
return html`
<atom-breadcrumb />
<script>
;(function () {
const breadcrumbsElements = document.querySelectorAll('atom-breadcrumb')
breadcrumbsElements.forEach((atomBreadcrumb) => {
atomBreadcrumb.items = [
{
text: 'First level',
title: 'Go to first level',
redirect: () => console.log('/first'),
},
{
text: 'Intermediary level',
title: 'Go to intermediary level',
redirect: () => console.log('/intermediary'),
},
{
text: 'Current level',
title: 'This is the current level',
},
]
})
})()
</script>
`
}

export const Default: StoryObj = {
render: () => createBreadcrumb(),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Meta, StoryObj } from '@storybook/react'
import React from 'react'

import { AtomBreadcrumb } from '@juntossomosmais/atomium/react'

import { BreadcrumbStoryArgs } from './breadcrumb.args'

export default {
title: 'Components/Breadcrumb',
...BreadcrumbStoryArgs,
} as Meta

const createBreadcrumb = (args) => (
<AtomBreadcrumb items={args.items}></AtomBreadcrumb>
)

export const Default: StoryObj = {
render: (args) => createBreadcrumb(args),
args: {
items: [
{
text: 'First level',
title: 'Go to first level',
redirect: () => console.log('/first'),
},
{
text: 'Intermediary level',
title: 'Go to intermediary level',
redirect: () => console.log('/intermediary'),
},
{
text: 'Current level',
title: 'This is the current level',
},
],
},
}
1 change: 1 addition & 0 deletions packages/icons/svg/mdi/arrow-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/icons/svg/mdi/west.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4ce3b96

Please sign in to comment.