Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(atomic): add atomic-tab-manager component #4196

Merged
merged 25 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ce13d7c
create tab-manager controller
fpbrault Jul 16, 2024
e86bcdb
remove clearFiltersOnTabChange from registerTab action
fpbrault Jul 17, 2024
94dee8c
remove triggersearch
fpbrault Jul 17, 2024
4ae52e5
rework atomic-tab-manager
fpbrault Jul 16, 2024
b1c830f
remove unused arrayprop
fpbrault Jul 16, 2024
15f74a4
add tabs page example
fpbrault Jul 16, 2024
8aaf0d5
remove triggersearch
fpbrault Jul 17, 2024
60ec970
use tailwind for styling of tab button
fpbrault Jul 18, 2024
199510f
Add generated files
Jul 18, 2024
c29829d
add parts for tab buttons
fpbrault Jul 18, 2024
0fba424
create tab-manager controller
fpbrault Jul 16, 2024
e8b8966
remove clearFiltersOnTabChange from registerTab action
fpbrault Jul 17, 2024
3b59bfb
remove triggersearch
fpbrault Jul 17, 2024
7086575
Merge branch 'CDX-1582' into CDX-1580
fpbrault Jul 19, 2024
01c3edf
clean up core tab manager
fpbrault Jul 23, 2024
addea59
use prepareForSearchWithQuery to clear filters
fpbrault Jul 23, 2024
b31c3f1
Merge branch 'CDX-1582' into CDX-1580
fpbrault Jul 23, 2024
080b6d3
create tab-manager controller
fpbrault Jul 16, 2024
50d6a79
remove clearFiltersOnTabChange from registerTab action
fpbrault Jul 17, 2024
6e9b573
remove triggersearch
fpbrault Jul 17, 2024
6a1dc6f
clean up core tab manager
fpbrault Jul 23, 2024
2d13740
use prepareForSearchWithQuery to clear filters
fpbrault Jul 23, 2024
66e3965
Merge branch 'CDX-1582' into CDX-1580
fpbrault Jul 23, 2024
529e2f3
Merge branch 'master' into CDX-1580
fpbrault Jul 23, 2024
b90c016
Merge branch 'master' into CDX-1580
fpbrault Jul 25, 2024
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
58 changes: 58 additions & 0 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3266,6 +3266,26 @@ export namespace Components {
*/
"label": string;
}
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 AtomicTabManager {
/**
* Whether to clear the filters when the active tab changes.
*/
"clearFiltersOnTabChange"?: boolean;
}
/**
* The `atomic-table-element` element defines a table column in a result list.
*/
Expand Down Expand Up @@ -5351,6 +5371,18 @@ declare global {
prototype: HTMLAtomicSortExpressionElement;
new (): HTMLAtomicSortExpressionElement;
};
interface HTMLAtomicTabElement extends Components.AtomicTab, HTMLStencilElement {
}
var HTMLAtomicTabElement: {
prototype: HTMLAtomicTabElement;
new (): HTMLAtomicTabElement;
};
interface HTMLAtomicTabManagerElement extends Components.AtomicTabManager, HTMLStencilElement {
}
var HTMLAtomicTabManagerElement: {
prototype: HTMLAtomicTabManagerElement;
new (): HTMLAtomicTabManagerElement;
};
/**
* The `atomic-table-element` element defines a table column in a result list.
*/
Expand Down Expand Up @@ -5584,6 +5616,8 @@ declare global {
"atomic-smart-snippet-suggestions": HTMLAtomicSmartSnippetSuggestionsElement;
"atomic-sort-dropdown": HTMLAtomicSortDropdownElement;
"atomic-sort-expression": HTMLAtomicSortExpressionElement;
"atomic-tab": HTMLAtomicTabElement;
"atomic-tab-manager": HTMLAtomicTabManagerElement;
"atomic-table-element": HTMLAtomicTableElementElement;
"atomic-text": HTMLAtomicTextElement;
"atomic-timeframe": HTMLAtomicTimeframeElement;
Expand Down Expand Up @@ -8640,6 +8674,26 @@ declare namespace LocalJSX {
*/
"label": string;
}
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 AtomicTabManager {
/**
* Whether to clear the filters when the active tab changes.
*/
"clearFiltersOnTabChange"?: boolean;
}
/**
* The `atomic-table-element` element defines a table column in a result list.
*/
Expand Down Expand Up @@ -8925,6 +8979,8 @@ declare namespace LocalJSX {
"atomic-smart-snippet-suggestions": AtomicSmartSnippetSuggestions;
"atomic-sort-dropdown": AtomicSortDropdown;
"atomic-sort-expression": AtomicSortExpression;
"atomic-tab": AtomicTab;
"atomic-tab-manager": AtomicTabManager;
"atomic-table-element": AtomicTableElement;
"atomic-text": AtomicText;
"atomic-timeframe": AtomicTimeframe;
Expand Down Expand Up @@ -9651,6 +9707,8 @@ declare module "@stencil/core" {
* The `atomic-sort-expression` component defines a sort expression. This component must be inside an `atomic-sort-dropdown` component.
*/
"atomic-sort-expression": LocalJSX.AtomicSortExpression & JSXBase.HTMLAttributes<HTMLAtomicSortExpressionElement>;
"atomic-tab": LocalJSX.AtomicTab & JSXBase.HTMLAttributes<HTMLAtomicTabElement>;
"atomic-tab-manager": LocalJSX.AtomicTabManager & JSXBase.HTMLAttributes<HTMLAtomicTabManagerElement>;
/**
* The `atomic-table-element` element defines a table column in a result list.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {FunctionalComponent, h} from '@stencil/core';
fpbrault marked this conversation as resolved.
Show resolved Hide resolved
import {Button} from '../button';

export interface TabButtonProps {
label: string | undefined;
handleClick: () => void;
isActive: boolean;
}

export const TabButton: FunctionalComponent<TabButtonProps> = (props) => {
const activeTabClass = props.isActive
? "relative after:content-[''] after:block after:w-full after:h-1 after:absolute after:-bottom-0.5 after:bg-primary after:rounded"
: '';
const activeTabTextClass = props.isActive ? '' : 'text-neutral-dark';
return (
<div part="button-container" class={activeTabClass}>
<Button
style="text-transparent"
class={`px-6 pb-1 w-full text-xl ${activeTabTextClass}`}
text={props.label}
part="button"
onClick={props.handleClick}
></Button>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {FunctionalComponent, h} from '@stencil/core';

interface TabDropdownOptionProps {
value: string;
label: string;
isSelected: boolean;
}

export const TabDropdownOption: FunctionalComponent<TabDropdownOptionProps> = (
props
) => {
return (
<option key={props.value} value={props.value} selected={props.isSelected}>
{props.label}
</option>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {FunctionalComponent, h} from '@stencil/core';

export interface TabDropdownProps {
tabs: Array<{name: string; label: string}>;
activeTab: string;
onTabChange: (tabName: string) => void;
}

export const TabDropdown: FunctionalComponent<TabDropdownProps> = (
props,
children
) => {
return (
<div class="hidden pb-1 border-b dropdown-area">
<select
class="py-2 text-xl font-bold cursor-pointer btn-text-primary"
onChange={(e) =>
props.onTabChange((e.target as HTMLSelectElement).value)
}
>
{children}
</select>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import '../../../../global/global.pcss';

@media (max-width: 768px) {
.dropdown-area {
display: block;
}

.tabs-area {
display: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
TabManager,
TabManagerState,
buildTabManager,
Tab,
buildTab,
} from '@coveo/headless';
import {Component, h, Element, State, Prop} from '@stencil/core';
import {
BindStateToController,
InitializeBindings,
} from '../../../../utils/initialization-utils';
import {TabButton} from '../../../common/tab-manager/tab-button';
import {TabDropdown} from '../../../common/tab-manager/tab-dropdown';
import {TabDropdownOption} from '../../../common/tab-manager/tab-dropdown-option';
import {Bindings} from '../../atomic-search-interface/atomic-search-interface';

/**
* @internal
*
* @part button-container - The container for the tab button.
* @part button - The tab button.
*/
@Component({
tag: 'atomic-tab-manager',
styleUrl: 'atomic-tab-manager.pcss',
shadow: true,
})
export class AtomicTabManager {
@InitializeBindings() public bindings!: Bindings;
@BindStateToController('tabManager')
@State()
private tabManagerState!: TabManagerState;
@Element()
private host!: HTMLElement;
public tabManager!: TabManager;

private tabs: {label: string; name: string; tabController: Tab}[] = [];

/**
* Whether to clear the filters when the active tab changes.
*/
@Prop() clearFiltersOnTabChange?: boolean = false;

@State() public error!: Error;

public initialize() {
this.tabManager = buildTabManager(this.bindings.engine);

const tabElements = Array.from(this.host.querySelectorAll('atomic-tab'));

if (tabElements.length === 0) {
this.error = new Error(
'The "atomic-tab-manager" element requires at least one "atomic-tab" child.'
);
return;
}

tabElements.forEach((tabElement, index) => {
const tabController = buildTab(this.bindings.engine, {
options: {
expression: tabElement.expression,
id: tabElement.name,
clearFiltersOnTabChange: this.clearFiltersOnTabChange,
},
initialState: {isActive: index === 0 ? true : false},
});

this.tabs.push({
label: tabElement.label,
name: tabElement.name,
tabController,
});
});
}

public render() {
return (
<div class="mb-2 overflow-x-auto">
<div class="flex flex-row w-full mb-2 border-b tabs-area">
{this.tabs.map((tab) => (
<TabButton
isActive={tab.tabController.state.isActive}
label={tab.label}
handleClick={() => {
if (!tab.tabController.state.isActive) {
tab.tabController.select();
}
}}
></TabButton>
))}
</div>
<TabDropdown
tabs={this.tabs}
activeTab={this.tabManagerState.activeTab}
onTabChange={(e) => {
const selectedTab = this.tabs.find(
(tab) => tab.name === (e as string)
);
if (selectedTab) {
selectedTab.tabController.select();
}
}}
>
{this.tabs.map((tab) => (
<TabDropdownOption
value={tab.name}
label={tab.label}
isSelected={tab.name === this.tabManagerState.activeTab}
/>
))}
</TabDropdown>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {Component, Prop, Element} from '@stencil/core';

/**
* @internal
*/
@Component({
tag: 'atomic-tab',
shadow: false,
})
export class AtomicTab {
@Element() public host!: HTMLElement;

/**
* The label to display on the tab.
*/
@Prop() label!: string;
/**
* The internal name of the atomic tab.
*/
@Prop() 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.
*/
@Prop() public expression: string = '';
}
Loading
Loading