Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/atomic-react/src/components/search/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
AtomicSearchBoxRecentQueries as LitAtomicSearchBoxRecentQueries,
AtomicSearchInterface as LitAtomicSearchInterface,
AtomicSearchLayout as LitAtomicSearchLayout,
AtomicTab as LitAtomicTab,
AtomicText as LitAtomicText,
} from '@coveo/atomic/components';
import {createComponent} from '@lit/react';
Expand Down Expand Up @@ -270,6 +271,12 @@ export const AtomicSearchLayout = createComponent({
elementClass: LitAtomicSearchLayout,
});

export const AtomicTab = createComponent({
tagName: 'atomic-tab',
react: React,
elementClass: LitAtomicTab,
});

export const AtomicText = createComponent({
tagName: 'atomic-text',
react: React,
Expand Down
53 changes: 0 additions & 53 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1942,24 +1942,6 @@ export namespace Components {
"side": 'left' | 'right';
"suggestion": SearchBoxSuggestionElement;
}
/**
* The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
* It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
interface AtomicTab {
/**
* The [constant query expression (`cq`)](https://docs.coveo.com/en/2830/searching-with-coveo/about-the-query-expression#constant-query-expression-cq) to apply when the tab is the active one.
*/
"expression": string;
/**
* The label to display on the tab.
*/
"label": string;
/**
* The internal name of the atomic tab.
*/
"name": string;
}
interface AtomicTabBar {
}
interface AtomicTabButton {
Expand Down Expand Up @@ -3199,16 +3181,6 @@ declare global {
prototype: HTMLAtomicSuggestionRendererElement;
new (): HTMLAtomicSuggestionRendererElement;
};
/**
* The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
* It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
interface HTMLAtomicTabElement extends Components.AtomicTab, HTMLStencilElement {
}
var HTMLAtomicTabElement: {
prototype: HTMLAtomicTabElement;
new (): HTMLAtomicTabElement;
};
interface HTMLAtomicTabBarElement extends Components.AtomicTabBar, HTMLStencilElement {
}
var HTMLAtomicTabBarElement: {
Expand Down Expand Up @@ -3369,7 +3341,6 @@ declare global {
"atomic-sort-expression": HTMLAtomicSortExpressionElement;
"atomic-stencil-facet-date-input": HTMLAtomicStencilFacetDateInputElement;
"atomic-suggestion-renderer": HTMLAtomicSuggestionRendererElement;
"atomic-tab": HTMLAtomicTabElement;
"atomic-tab-bar": HTMLAtomicTabBarElement;
"atomic-tab-button": HTMLAtomicTabButtonElement;
"atomic-tab-manager": HTMLAtomicTabManagerElement;
Expand Down Expand Up @@ -5257,24 +5228,6 @@ declare namespace LocalJSX {
"side": 'left' | 'right';
"suggestion": SearchBoxSuggestionElement;
}
/**
* The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
* It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
interface AtomicTab {
/**
* The [constant query expression (`cq`)](https://docs.coveo.com/en/2830/searching-with-coveo/about-the-query-expression#constant-query-expression-cq) to apply when the tab is the active one.
*/
"expression"?: string;
/**
* The label to display on the tab.
*/
"label": string;
/**
* The internal name of the atomic tab.
*/
"name": string;
}
interface AtomicTabBar {
}
interface AtomicTabButton {
Expand Down Expand Up @@ -5499,7 +5452,6 @@ declare namespace LocalJSX {
"atomic-sort-expression": AtomicSortExpression;
"atomic-stencil-facet-date-input": AtomicStencilFacetDateInput;
"atomic-suggestion-renderer": AtomicSuggestionRenderer;
"atomic-tab": AtomicTab;
"atomic-tab-bar": AtomicTabBar;
"atomic-tab-button": AtomicTabButton;
"atomic-tab-manager": AtomicTabManager;
Expand Down Expand Up @@ -5869,11 +5821,6 @@ declare module "@stencil/core" {
* use native Elements.
*/
"atomic-suggestion-renderer": LocalJSX.AtomicSuggestionRenderer & JSXBase.HTMLAttributes<HTMLAtomicSuggestionRendererElement>;
/**
* The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
* It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
"atomic-tab": LocalJSX.AtomicTab & JSXBase.HTMLAttributes<HTMLAtomicTabElement>;
"atomic-tab-bar": LocalJSX.AtomicTabBar & JSXBase.HTMLAttributes<HTMLAtomicTabBarElement>;
"atomic-tab-button": LocalJSX.AtomicTabButton & JSXBase.HTMLAttributes<HTMLAtomicTabButtonElement>;
/**
Expand Down
40 changes: 40 additions & 0 deletions packages/atomic/src/components/search/atomic-tab/atomic-tab.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Meta } from '@storybook/addon-docs/blocks';
import * as AtomicTabStories from './atomic-tab.new.stories';
import { AtomicDocTemplate } from '../../../../storybook-utils/documentation/atomic-doc-template';


<Meta of={AtomicTabStories} />

<AtomicDocTemplate
stories={AtomicTabStories}
githubPath="search/atomic-tab/atomic-tab.ts"
tagName="atomic-tab"
className="AtomicTab"
>

The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
It must be used as a child of the `atomic-tab-manager` component to function correctly.

This component is not used standalone. It must be a child of `atomic-tab-manager`.

See the [atomic-tab-manager documentation](?path=/docs/atomic-tab-manager--docs) for additional usage examples.

```html
<atomic-search-interface>
<!-- other components -->

<atomic-tab-manager>
<atomic-tab name="all" label="All"></atomic-tab>
<atomic-tab name="images" label="Images" expression="@objecttype==Image"></atomic-tab>
<atomic-tab name="articles" label="Articles" expression="@objecttype==Article"></atomic-tab>
</atomic-tab-manager>

<!-- other components -->
</atomic-search-interface>
```

## Related Components

- [atomic-tab-manager](?path=/docs/atomic-tab-manager--docs): Manages a collection of tabs and allows users to switch between them

</AtomicDocTemplate>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type {Meta, StoryObj as Story} from '@storybook/web-components-vite';
import {getStorybookHelpers} from '@wc-toolkit/storybook-helpers';
import {html} from 'lit';
import {parameters} from '@/storybook-utils/common/common-meta-parameters';
import {wrapInSearchInterface} from '@/storybook-utils/search/search-interface-wrapper';

const {decorator, play} = wrapInSearchInterface();

const {events, argTypes} = getStorybookHelpers('atomic-tab', {
excludeCategories: ['methods'],
});

const meta: Meta = {
component: 'atomic-tab',
title: 'Search/Tab/atomic-tab',
id: 'atomic-tab',
render: () => html`<atomic-tab-manager>
<atomic-tab
label="All"
name="all"
></atomic-tab>
<atomic-tab
label="Images"
name="images"
></atomic-tab>
<atomic-tab
label="Articles"
name="articles"
></atomic-tab>
</atomic-tab-manager>`,
decorators: [decorator],
parameters: {
...parameters,
actions: {
handles: events,
},
},
argTypes,
play,
};

export default meta;

export const Default: Story = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {html} from 'lit';
import {describe, expect, it} from 'vitest';
import {fixture} from '@/vitest-utils/testing-helpers/fixture';
import type {AtomicTab} from './atomic-tab';
import './atomic-tab';

describe('atomic-tab', () => {
const renderTab = async ({
label = 'Test Tab',
name = 'test-tab',
expression = '',
} = {}) => {
const element = await fixture<AtomicTab>(html`
<atomic-tab
label="${label}"
name="${name}"
expression="${expression}"
></atomic-tab>
`);

return {element};
};

describe('when rendering with valid props', () => {
it('should render successfully with required props', async () => {
const {element} = await renderTab();
expect(element).toBeInTheDocument();
});

it('should render successfully with all props', async () => {
const {element} = await renderTab({
label: 'My Tab',
name: 'my-tab',
expression: '@source==MySource',
});
expect(element).toBeInTheDocument();
});

it('should reflect label property to attribute', async () => {
const {element} = await renderTab({label: 'Custom Label'});
expect(element.getAttribute('label')).toBe('Custom Label');
});

it('should reflect name property to attribute', async () => {
const {element} = await renderTab({name: 'custom-name'});
expect(element.getAttribute('name')).toBe('custom-name');
});

it('should reflect expression property to attribute', async () => {
const {element} = await renderTab({expression: '@source==Test'});
expect(element.getAttribute('expression')).toBe('@source==Test');
});

it('should have empty expression by default', async () => {
const {element} = await renderTab();
expect(element.expression).toBe('');
});

it('should allow updating properties', async () => {
const {element} = await renderTab();

element.label = 'Updated Label';
element.name = 'updated-name';
element.expression = '@updated==true';
await element.updateComplete;

expect(element.label).toBe('Updated Label');
expect(element.name).toBe('updated-name');
expect(element.expression).toBe('@updated==true');
});
});

describe('when rendering slots', () => {
it('should render default slot content', async () => {
const element = await fixture<AtomicTab>(html`
<atomic-tab label="Test" name="test">
<div id="slot-content">Slot Content</div>
</atomic-tab>
`);

const slotContent = element.querySelector('#slot-content');
expect(slotContent).toBeInTheDocument();
expect(slotContent?.textContent).toBe('Slot Content');
});
});
});
55 changes: 55 additions & 0 deletions packages/atomic/src/components/search/atomic-tab/atomic-tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {Schema, StringValue} from '@coveo/bueno';
import {html, LitElement} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {ValidatePropsController} from '@/src/components/common/validate-props-controller/validate-props-controller';
import {LightDomMixin} from '@/src/mixins/light-dom';

/**
* The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
* It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
@customElement('atomic-tab')
export class AtomicTab extends LightDomMixin(LitElement) {
/**
* The label to display on the tab.
*/
@property({type: String, reflect: true}) label!: string;

/**
* The internal name of the atomic tab.
*/
@property({type: String, reflect: true}) name!: string;

/**
* The [constant query expression (`cq`)](https://docs.coveo.com/en/2830/searching-with-coveo/about-the-query-expression#constant-query-expression-cq) to apply when the tab is the active one.
*/
@property({type: String, reflect: true}) public expression: string = '';

@state() public error!: Error;

constructor() {
super();

new ValidatePropsController(
this,
() => ({
label: this.label,
name: this.name,
}),
new Schema({
label: new StringValue({required: true, emptyAllowed: false}),
name: new StringValue({required: true, emptyAllowed: false}),
})
);
}

render() {
return html`<slot></slot>`;
}
}

declare global {
interface HTMLElementTagNameMap {
'atomic-tab': AtomicTab;
}
}
1 change: 1 addition & 0 deletions packages/atomic/src/components/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ export {AtomicSearchBoxQuerySuggestions} from './atomic-search-box-query-suggest
export {AtomicSearchBoxRecentQueries} from './atomic-search-box-recent-queries/atomic-search-box-recent-queries.js';
export {AtomicSearchInterface} from './atomic-search-interface/atomic-search-interface.js';
export {AtomicSearchLayout} from './atomic-search-layout/atomic-search-layout.js';
export {AtomicTab} from './atomic-tab/atomic-tab.js';
export {AtomicText} from './atomic-text/atomic-text.js';
1 change: 1 addition & 0 deletions packages/atomic/src/components/search/lazy-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export default {
await import('./atomic-search-interface/atomic-search-interface.js'),
'atomic-search-layout': async () =>
await import('./atomic-search-layout/atomic-search-layout.js'),
'atomic-tab': async () => await import('./atomic-tab/atomic-tab.js'),
'atomic-text': async () => await import('./atomic-text/atomic-text.js'),
} as Record<string, () => Promise<unknown>>;

Expand Down

This file was deleted.

Loading