Skip to content

Commit

Permalink
feat: tab component (#160)
Browse files Browse the repository at this point in the history
* feat(tab): init bl-tab, bl-tab-panel, bl-tab-group

* feat(tab): added tab css, borders css

* feat(tab): added var for css rules

* feat(tab): added ::after,::before pseudo for borders

* feat(tab): rename active prop to selected

* feat(tab): added button for tab element

* feat: tab styling

* feat(tab): added help text, badge

* feat: tab notify, caption, icon added

* feat(tab): refactor notify

* feat(tab): added hover event for help text

* fix(tab): review issues

* fix(tab): remove other component declaration in tab, set panel as protected prop in tab

* fix(tab): set name as protected prop in tab

* test(tab): added test

* test(tab): added test for tab-panel

* test(tab): added test for tab-panel

* test(tab): added hover effect

* test(tab): complete tests

* test(tab): fixed disabled and selected case in css

* test(tab): fixed disabled badge color

* test(tab): use expect instead of assert

* test(tab): fix linter

* test(tab): fix linter for tooltip

* fix(tab): sonar issue

* fix(tab): linter after sonar issue fixed

* feat(tab): storybook for tab,tab panel and tab group completed

* feat(tab): tab scroll bug fixed

* feat(tab): wording changes

* refactor(button): reverted changes in bl-button.ts

* refactor: review resolves

* refactor: lint

* feat(tab): added JSDoc

* feat(tab): added unregister function

* refactor: fix lint

* test(tab): icon mock in test

* fix(tab): fixed lint

* feat(tab): tab component's storybook is detailed

* test(tab): added add/remove tab tests

* feat(tab): unnecessary code snippets removed form tab storybooks

* fix(tab): set tooltip placement as default

* refactor: tooltip style moved to css file

* fix(tab): remove title attribute

* fix(tab): css variables

* fix(tab): linter format

* fix(tab): remove tab title from tab-panel and tab-group stories

* fix(tab): 1px margin bottom for icon notify and badge

Co-authored-by: mehmet.tanas <[email protected]>
Co-authored-by: erdem.besler <[email protected]>
Co-authored-by: Murat Çorlu <[email protected]>
Co-authored-by: Murat Çorlu <[email protected]>
  • Loading branch information
5 people authored Aug 9, 2022
1 parent 87b986b commit 3491805
Show file tree
Hide file tree
Showing 15 changed files with 1,040 additions and 3 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'icon',
'input',
'badge',
'tab',
'tooltip'
],
],
Expand Down
7 changes: 5 additions & 2 deletions src/baklava.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export { default as BlIcon } from './components/icon/bl-icon';
export { default as BlButton } from './components/button/bl-button';
export { default as BlInput } from './components/input/bl-input';
export { default as BlBadge } from './components/badge/bl-badge';
export { default as BlTooltip } from './components/tooltip/bl-tooltip';
export { default as BlInput } from './components/input/bl-input';
export { default as BlTab } from './components/tab-group/tab/bl-tab';
export { default as BlTabGroup } from './components/tab-group/bl-tab-group';
export { default as BlTabPanel } from './components/tab-group/tab-panel/bl-tab-panel';
export { getIconPath, setIconPath } from './utilities/asset-paths';
export { default as BlTooltip } from './components/tooltip/bl-tooltip';
2 changes: 1 addition & 1 deletion src/components/button/bl-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class BlButton extends LitElement {
}

render(): TemplateResult {
const isAnchor = this.href ? true : false;
const isAnchor = !!this.href;
const icon = this.icon ? html`<bl-icon name=${this.icon}></bl-icon>` : '';
const slots = html`<slot name="icon">${icon}</slot> <span class="label"><slot></slot></span>`;
const classes = classMap({
Expand Down
18 changes: 18 additions & 0 deletions src/components/tab-group/bl-tab-group.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.tabs {
background-color: var(--bl-color-primary-background);
border-bottom: var(--bl-size-4xs) solid var(--bl-color-secondary-background);
display: flex;
flex-direction: row;
}

.tabs-list {
overflow-x: scroll;

/* FIXME: use variables */
border-radius: 6px 6px 0 0;
}

.panels {
/* FIXME: use variables */
border-radius: 0 0 6px 6px;
}
85 changes: 85 additions & 0 deletions src/components/tab-group/bl-tab-group.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { Meta, Canvas, ArgsTable, Story, Preview, Source } from '@storybook/addon-docs';

<Meta
title="Components/TabGroup"
component="bl-tab-group"
argTypes={{
icon: {
control: 'text'
}
}}
/>

export const TabGroup = (args) => html`
<bl-tab-group>
<bl-tab name="test-1" slot="tabs" disabled>Disabled</bl-tab>
<bl-tab name="test-2" slot="tabs" selected >Selected</bl-tab>
<bl-tab name="test-3" slot="tabs" >Tab</bl-tab>
<bl-tab-panel tab="test-1">Panel 1</bl-tab-panel>
<bl-tab-panel tab="test-2">Panel 2</bl-tab-panel>
<bl-tab-panel tab="test-3">Panel 3</bl-tab-panel>
</bl-tab-group>
`
export const ScrollableTabGroup = (args) => html`
<bl-tab-group>
<bl-tab name="tab-1" slot="tabs">Tab 1</bl-tab>
<bl-tab name="tab-2" slot="tabs">Tab 2</bl-tab>
<bl-tab name="tab-3" slot="tabs">Tab 3</bl-tab>
<bl-tab name="tab-4" slot="tabs">Tab 4</bl-tab>
<bl-tab name="tab-5" slot="tabs">Tab 5</bl-tab>
<bl-tab name="tab-6" slot="tabs">Tab 6</bl-tab>
<bl-tab name="tab-7" slot="tabs">Tab 7</bl-tab>
<bl-tab name="tab-8" slot="tabs">Tab 8</bl-tab>
<bl-tab name="tab-9" slot="tabs">Tab 9</bl-tab>
<bl-tab name="tab-10" slot="tabs">Tab 10</bl-tab>
<bl-tab name="tab-11" slot="tabs">Tab 11</bl-tab>
<bl-tab name="tab-12" slot="tabs">Tab 12</bl-tab>
<bl-tab name="tab-13" slot="tabs">Tab 13</bl-tab>
<bl-tab name="tab-14" slot="tabs">Tab 14</bl-tab>
<bl-tab name="tab-15" slot="tabs">Tab 15</bl-tab>
<bl-tab name="tab-16" slot="tabs">Tab 16</bl-tab>
<bl-tab name="tab-17" slot="tabs">Tab 17</bl-tab>
<bl-tab name="tab-18" slot="tabs">Tab 18</bl-tab>
<bl-tab-panel tab="tab-1">Panel 1</bl-tab-panel>
<bl-tab-panel tab="tab-2">Panel 2</bl-tab-panel>
<bl-tab-panel tab="tab-3">Panel 3</bl-tab-panel>
<bl-tab-panel tab="tab-4">Panel 4</bl-tab-panel>
<bl-tab-panel tab="tab-5">Panel 5</bl-tab-panel>
<bl-tab-panel tab="tab-6">Panel 6</bl-tab-panel>
<bl-tab-panel tab="tab-7">Panel 7</bl-tab-panel>
<bl-tab-panel tab="tab-8">Panel 8</bl-tab-panel>
<bl-tab-panel tab="tab-9">Panel 9</bl-tab-panel>
<bl-tab-panel tab="tab-10">Panel 10</bl-tab-panel>
<bl-tab-panel tab="tab-11">Panel 11</bl-tab-panel>
<bl-tab-panel tab="tab-12">Panel 12</bl-tab-panel>
<bl-tab-panel tab="tab-13">Panel 13</bl-tab-panel>
<bl-tab-panel tab="tab-14">Panel 14</bl-tab-panel>
<bl-tab-panel tab="tab-15">Panel 15</bl-tab-panel>
<bl-tab-panel tab="tab-16">Panel 16</bl-tab-panel>
<bl-tab-panel tab="tab-17">Panel 17</bl-tab-panel>
<bl-tab-panel tab="tab-18">Panel 18</bl-tab-panel>
</bl-tab-group>
`

# Tab Group
Tab groups organizes the content in a way that each of Tab groups displays a section at a time.

<Canvas>
<Story name="Basic Usage">
{TabGroup.bind({})}
</Story>
</Canvas>

## Scrolling Tabs
The navigation will scroll if there are more tabs than the horizontal space can allow.

<Canvas>
<Story name="Scroll Usage">
{ScrollableTabGroup.bind({})}
</Story>
</Canvas>

188 changes: 188 additions & 0 deletions src/components/tab-group/bl-tab-group.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { fixture, html, fixtureCleanup, expect, nextFrame } from '@open-wc/testing';
import BlTabGroup from './bl-tab-group';

const createTab = () => {
const tabName = Date.now().toString();
const tab = document.createElement('bl-tab');
tab.slot = 'tabs';
tab.name = tabName;
tab.title = 'Add Player';

return tab;
};

describe('bl-tab-group', function () {
afterEach(() => {
fixtureCleanup();
});
it('should defined', async () => {
const el = document.createElement('bl-tab-group');
expect(el).to.be.instanceof(BlTabGroup);
});

it('render with default values', async function () {
const expected = `
<div class="container">
<div role="tablist" class="tabs-list">
<div class="tabs">
<slot name="tabs"></slot>
</div>
</div>
<div role="tabpanel" class="panels">
<slot></slot>
</div>
</div>
`;
const el = await fixture<BlTabGroup>(html` <bl-tab-group></bl-tab-group>`);
expect(el).to.be.shadowDom.equal(expected);
});

it('should render panels', async function () {
const el = await fixture<BlTabGroup>(
html` <bl-tab-group>
<bl-tab name="test" slot="tabs">Test Tab</bl-tab>
<bl-tab-panel tab="test"></bl-tab-panel>
</bl-tab-group>`
);

expect(el.tabs.length).to.be.equal(1);
});

it('should select correct tab if has selected attr', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs">Test 1 Tab</bl-tab>
<bl-tab name="test-2" slot="tabs" selected>Test 2 Tab</bl-tab>
<bl-tab-panel tab="test-1"></bl-tab-panel>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
const selectedPanel = el.panels.find(p => p.tab === 'test-2');

expect(el.selectedTabName).to.be.equal('test-2');
expect(selectedPanel?.visible).to.be.true;
});

it('should handle bl-tab-selected event', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs">Test 1 Tab</bl-tab>
<bl-tab name="test-2" slot="tabs" selected>Test 2 Tab</bl-tab>
<bl-tab-panel tab="test-1"></bl-tab-panel>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
el.tabs[0].select();
expect(el.selectedTabName).to.be.equal('test-1');
expect(el.tabs[0].selected).to.be.true;
expect(el.tabs[1].selected).to.be.false;
expect(el.panels.find(p => p.tab === el.tabs[0].name)?.visible).to.be.true;
expect(el.panels.find(p => p.tab === el.tabs[1].name)?.visible).to.be.false;
});
});

describe('should selected tab functionality works when add or remove tabs ', function () {
it('should new tab selected', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs">Test 1 Tab</bl-tab>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
expect(el.tabs[0].selected).to.be.true;

// add new tab with selected flag
const tab = createTab();
tab.selected = true;
el.appendChild(tab);

await nextFrame();

expect(el.tabs.length).to.be.equal(2);
expect(el.tabs[0].selected).to.be.false;
expect(el.tabs[1].selected).to.be.true;
});

it('add a tab with disabled flag', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs">Test 1 Tab</bl-tab>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
expect(el.tabs[0].selected).to.be.true;

// add new tab with disabled flag
const tab = createTab();
tab.disabled = true;
el.appendChild(tab);

await nextFrame();

expect(el.tabs.length).to.be.equal(2);
expect(el.tabs[0].selected).to.be.true;
expect(el.tabs[1].selected).to.be.false;
});

it('first tab is disabled', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs" disabled>Test 1 Tab</bl-tab>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
expect(el.tabs[0].selected).to.be.false;

// add new tab
const tab = createTab();
el.appendChild(tab);

await nextFrame();

expect(el.tabs.length).to.be.equal(2);
expect(el.tabs[0].selected).to.be.false;
expect(el.tabs[1].selected).to.be.true;
});

it('added two tabs that first is disabled and second is selected', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs" disabled>Test 1 Tab</bl-tab>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);
expect(el.tabs[0].selected).to.be.false;

// add new tabs
const disabledTab = createTab();
const selectedTab = createTab();
disabledTab.disabled = true;
selectedTab.selected = true;
el.appendChild(disabledTab);
el.appendChild(selectedTab);

await nextFrame();

expect(el.tabs.length).to.be.equal(3);
expect(el.tabs[0].selected).to.be.false;
expect(el.tabs[2].selected).to.be.true;
});

it('add a disabled and selected tab then remove selected tab', async function () {
const el = await fixture<BlTabGroup>(html` <bl-tab-group>
<bl-tab name="test-1" slot="tabs">Test 1 Tab</bl-tab>
<bl-tab-panel tab="test-2"></bl-tab-panel>
</bl-tab-group>`);

// add new tabs
const disabledTab = createTab();
const selectedTab = createTab();
disabledTab.disabled = true;
selectedTab.selected = true;
el.appendChild(disabledTab);
await nextFrame();
expect(el.tabs[0].selected).to.be.true;
el.appendChild(selectedTab);
await nextFrame();
expect(el.tabs[0].selected).to.be.false;
expect(el.tabs[2].selected).to.be.true;

// remove last tab that selected
el.removeChild(el.tabs[el.tabs.length - 1]);

await nextFrame();

expect(el.tabs.length).to.be.equal(2);
expect(el.tabs[0].selected).to.be.true;
expect(el.tabs[1].selected).to.be.false;
expect(el.tabs[1].disabled).to.be.true;
});
});
Loading

0 comments on commit 3491805

Please sign in to comment.