Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1153252
Initial plan
Copilot Dec 12, 2025
942fb1b
feat(atomic): migrate atomic-timeframe component to Lit
Copilot Dec 12, 2025
515802f
test(atomic): add comprehensive unit tests for atomic-timeframe
Copilot Dec 12, 2025
d4acc8c
docs(atomic): add MDX documentation for atomic-timeframe
Copilot Dec 12, 2025
ad69289
chore(atomic): update auto-generated files for atomic-timeframe
Copilot Dec 12, 2025
9df4a0e
Merge branch 'main' into copilot/migrate-atomic-timeframe
fbeaudoincoveo Dec 15, 2025
245d2ea
Add generated files
developer-experience-bot[bot] Dec 15, 2025
c0ddb31
refactor(atomic): add validation and error handling to atomic-timeframe
Copilot Dec 15, 2025
0cc8621
refactor(atomic): improve atomic-timeframe tests and JSDoc
Copilot Dec 15, 2025
5f1568d
Merge branch 'main' into copilot/migrate-atomic-timeframe
fbeaudoincoveo Dec 16, 2025
5f31800
Add generated files
developer-experience-bot[bot] Dec 16, 2025
070e914
reorder atomic-timeframe import in lazy-index
fbeaudoincoveo Dec 16, 2025
814d35d
Merge branch 'copilot/migrate-atomic-timeframe' of https://github.com…
fbeaudoincoveo Dec 16, 2025
5df4a32
Improve typing
fbeaudoincoveo Dec 16, 2025
ee47f5e
Update comments in atomic-timeframe component for clarity
fbeaudoincoveo Dec 16, 2025
6b41383
refactor(atomic): add MSW API mocking to atomic-timeframe stories
Copilot Dec 16, 2025
82eeb11
Fix stories
fbeaudoincoveo Dec 17, 2025
92a9d2a
Update docs
fbeaudoincoveo Dec 17, 2025
19bd431
Merge branch 'main' into copilot/migrate-atomic-timeframe
fbeaudoincoveo Dec 17, 2025
8c80d46
Commit generated files
fbeaudoincoveo Dec 17, 2025
d36076a
refactor(atomic): simplify period test to create element with desired…
Copilot Dec 17, 2025
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/commerce/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
AtomicProductSectionName as LitAtomicProductSectionName,
AtomicProductSectionVisual as LitAtomicProductSectionVisual,
AtomicProductText as LitAtomicProductText,
AtomicTimeframe as LitAtomicTimeframe,
} from '@coveo/atomic/components';
import {createComponent} from '@lit/react';
import React from 'react';
Expand Down Expand Up @@ -387,3 +388,9 @@ export const AtomicProductText = createComponent({
react: React,
elementClass: LitAtomicProductText,
});

export const AtomicTimeframe = createComponent({
tagName: 'atomic-timeframe',
react: React,
elementClass: LitAtomicTimeframe,
});
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 @@ -69,6 +69,7 @@ import {
AtomicTableElement as LitAtomicTableElement,
AtomicTabManager as LitAtomicTabManager,
AtomicText as LitAtomicText,
AtomicTimeframe as LitAtomicTimeframe,
} from '@coveo/atomic/components';
import {createComponent} from '@lit/react';
import React from 'react';
Expand Down Expand Up @@ -492,3 +493,9 @@ export const AtomicText = createComponent({
react: React,
elementClass: LitAtomicText,
});

export const AtomicTimeframe = createComponent({
tagName: 'atomic-timeframe',
react: React,
elementClass: LitAtomicTimeframe,
});
65 changes: 2 additions & 63 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, FacetSortCriterion, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, RelativeDateUnit, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless";
import { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, FacetSortCriterion, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless";
import { AnyBindings } from "./components/common/interface/bindings";
import { NumberInputType } from "./components/common/facets/facet-number-input/number-input-type";
import { FacetSortCriterion as InsightFacetSortCriterion, FoldedResult as InsightFoldedResult, InteractiveResult as InsightInteractiveResult, RangeFacetRangeAlgorithm as InsightRangeFacetRangeAlgorithm, RangeFacetSortCriterion as InsightRangeFacetSortCriterion, Result as InsightResult, ResultTemplate as InsightResultTemplate, ResultTemplateCondition as InsightResultTemplateCondition, UserAction as IUserAction } from "@coveo/headless/insight";
Expand All @@ -19,7 +19,7 @@ import { RecsStore } from "./components/recommendations/atomic-recs-interface/st
import { RedirectionPayload } from "./components/common/search-box/redirection-payload";
import { i18n } from "i18next";
import { SearchBoxSuggestionElement } from "./components/common/suggestions/suggestions-types";
export { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, FacetSortCriterion, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, RelativeDateUnit, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless";
export { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, FacetSortCriterion, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless";
export { AnyBindings } from "./components/common/interface/bindings";
export { NumberInputType } from "./components/common/facets/facet-number-input/number-input-type";
export { FacetSortCriterion as InsightFacetSortCriterion, FoldedResult as InsightFoldedResult, InteractiveResult as InsightInteractiveResult, RangeFacetRangeAlgorithm as InsightRangeFacetRangeAlgorithm, RangeFacetSortCriterion as InsightRangeFacetSortCriterion, Result as InsightResult, ResultTemplate as InsightResultTemplate, ResultTemplateCondition as InsightResultTemplateCondition, UserAction as IUserAction } from "@coveo/headless/insight";
Expand Down Expand Up @@ -1296,28 +1296,6 @@ export namespace Components {
"setButtonVisibility": (isVisible: boolean) => Promise<void>;
"toggle": () => Promise<void>;
}
/**
* The `atomic-timeframe` component defines a timeframe of an `atomic-timeframe-facet`, and therefore must be defined within an `atomic-timeframe-facet` component.
* A timeframe is a span of time from now to a specific time in the past.
*/
interface AtomicTimeframe {
/**
* The amount of units from which to count. For example, 10 days, 1 year, etc.
*/
"amount": number;
/**
* The non-localized label for the timeframe. When defined, it will appear instead of the formatted value. Used in the `atomic-breadbox` component through the bindings store.
*/
"label"?: string;
/**
* The relative period of time to now.
*/
"period": 'past' | 'next';
/**
* The unit used to define: - the start date of the timeframe, if the period is `past` - the end date of the timeframe, if the period is `future`
*/
"unit": RelativeDateUnit;
}
/**
* A facet is a list of values for a certain field occurring in the results.
* An `atomic-timeframe-facet` displays a facet of the results for the current query as date intervals.
Expand Down Expand Up @@ -2208,16 +2186,6 @@ declare global {
prototype: HTMLAtomicTabPopoverElement;
new (): HTMLAtomicTabPopoverElement;
};
/**
* The `atomic-timeframe` component defines a timeframe of an `atomic-timeframe-facet`, and therefore must be defined within an `atomic-timeframe-facet` component.
* A timeframe is a span of time from now to a specific time in the past.
*/
interface HTMLAtomicTimeframeElement extends Components.AtomicTimeframe, HTMLStencilElement {
}
var HTMLAtomicTimeframeElement: {
prototype: HTMLAtomicTimeframeElement;
new (): HTMLAtomicTimeframeElement;
};
/**
* A facet is a list of values for a certain field occurring in the results.
* An `atomic-timeframe-facet` displays a facet of the results for the current query as date intervals.
Expand Down Expand Up @@ -2301,7 +2269,6 @@ declare global {
"atomic-tab-bar": HTMLAtomicTabBarElement;
"atomic-tab-button": HTMLAtomicTabButtonElement;
"atomic-tab-popover": HTMLAtomicTabPopoverElement;
"atomic-timeframe": HTMLAtomicTimeframeElement;
"atomic-timeframe-facet": HTMLAtomicTimeframeFacetElement;
}
}
Expand Down Expand Up @@ -3546,28 +3513,6 @@ declare namespace LocalJSX {
}
interface AtomicTabPopover {
}
/**
* The `atomic-timeframe` component defines a timeframe of an `atomic-timeframe-facet`, and therefore must be defined within an `atomic-timeframe-facet` component.
* A timeframe is a span of time from now to a specific time in the past.
*/
interface AtomicTimeframe {
/**
* The amount of units from which to count. For example, 10 days, 1 year, etc.
*/
"amount"?: number;
/**
* The non-localized label for the timeframe. When defined, it will appear instead of the formatted value. Used in the `atomic-breadbox` component through the bindings store.
*/
"label"?: string;
/**
* The relative period of time to now.
*/
"period"?: 'past' | 'next';
/**
* The unit used to define: - the start date of the timeframe, if the period is `past` - the end date of the timeframe, if the period is `future`
*/
"unit": RelativeDateUnit;
}
/**
* A facet is a list of values for a certain field occurring in the results.
* An `atomic-timeframe-facet` displays a facet of the results for the current query as date intervals.
Expand Down Expand Up @@ -3703,7 +3648,6 @@ declare namespace LocalJSX {
"atomic-tab-bar": AtomicTabBar;
"atomic-tab-button": AtomicTabButton;
"atomic-tab-popover": AtomicTabPopover;
"atomic-timeframe": AtomicTimeframe;
"atomic-timeframe-facet": AtomicTimeframeFacet;
}
}
Expand Down Expand Up @@ -3926,11 +3870,6 @@ declare module "@stencil/core" {
"atomic-tab-bar": LocalJSX.AtomicTabBar & JSXBase.HTMLAttributes<HTMLAtomicTabBarElement>;
"atomic-tab-button": LocalJSX.AtomicTabButton & JSXBase.HTMLAttributes<HTMLAtomicTabButtonElement>;
"atomic-tab-popover": LocalJSX.AtomicTabPopover & JSXBase.HTMLAttributes<HTMLAtomicTabPopoverElement>;
/**
* The `atomic-timeframe` component defines a timeframe of an `atomic-timeframe-facet`, and therefore must be defined within an `atomic-timeframe-facet` component.
* A timeframe is a span of time from now to a specific time in the past.
*/
"atomic-timeframe": LocalJSX.AtomicTimeframe & JSXBase.HTMLAttributes<HTMLAtomicTimeframeElement>;
/**
* A facet is a list of values for a certain field occurring in the results.
* An `atomic-timeframe-facet` displays a facet of the results for the current query as date intervals.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Meta } from '@storybook/addon-docs/blocks';
import * as AtomicTimeframeStories from './atomic-timeframe.new.stories';
import { AtomicDocTemplate } from '@/storybook-utils/documentation/atomic-doc-template';


<Meta of={AtomicTimeframeStories} />

<AtomicDocTemplate
stories={AtomicTimeframeStories}
githubPath="common/atomic-timeframe/atomic-timeframe.ts"
tagName="atomic-timeframe"
className="AtomicTimeframe"
>

## Usage

The `atomic-timeframe` component defines a timeframe for an `atomic-timeframe-facet` component. It must be defined within an `atomic-timeframe-facet` component to function properly.

A timeframe represents a span of time from now to a specific time in the past (or future).

### Basic Example

```html
<atomic-timeframe-facet field="date">
<atomic-timeframe unit="day"></atomic-timeframe>
<atomic-timeframe unit="week"></atomic-timeframe>
<atomic-timeframe unit="month"></atomic-timeframe>
<atomic-timeframe unit="year"></atomic-timeframe>
</atomic-timeframe-facet>
```

### With Custom Amounts

```html
<atomic-timeframe-facet field="date">
<atomic-timeframe unit="day" amount="7" label="Last week"></atomic-timeframe>
<atomic-timeframe unit="month" amount="3" label="Last quarter"></atomic-timeframe>
<atomic-timeframe unit="year" amount="1" label="Last year"></atomic-timeframe>
</atomic-timeframe-facet>
```

### Future Timeframes

```html
<atomic-timeframe-facet field="date">
<atomic-timeframe period="next" unit="day" amount="7" label="Next week"></atomic-timeframe>
<atomic-timeframe period="next" unit="month" label="Next month"></atomic-timeframe>
</atomic-timeframe-facet>
```

## Related Components

- [atomic-timeframe-facet](../?path=/docs/atomic-timeframe-facet--default) - Search timeframe facet
- [atomic-commerce-timeframe-facet](../?path=/docs/atomic-commerce-timeframe-facet--default) - Commerce timeframe facet
- [atomic-insight-timeframe-facet](../?path=/docs/atomic-insight-timeframe-facet--default) - Insight timeframe facet

</AtomicDocTemplate>
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {html} from 'lit';
import {describe, expect, it} from 'vitest';
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import type {AtomicTimeframe} from './atomic-timeframe';
import './atomic-timeframe';

describe('atomic-timeframe', () => {
const renderTimeframe = async ({
period = 'past',
unit = 'day',
amount = 1,
label,
}: {
period?: 'past' | 'next';
unit?: string;
amount?: number;
label?: string;
} = {}) => {
const element = await renderFunctionFixture(html`
<atomic-timeframe
period=${period}
unit=${unit}
amount=${amount}
label=${label ?? ''}
></atomic-timeframe>
`);

const timeframe = element.querySelector(
'atomic-timeframe'
) as AtomicTimeframe;

return {element, timeframe};
};

describe('default properties', () => {
it('should have default period of "past"', async () => {
const {timeframe} = await renderTimeframe();
expect(timeframe.period).toBe('past');
});

it('should have default amount of 1', async () => {
const {timeframe} = await renderTimeframe();
expect(timeframe.amount).toBe(1);
});
});

describe('when setting period', () => {
it('should set period to "past"', async () => {
const {timeframe} = await renderTimeframe({period: 'past'});
expect(timeframe.period).toBe('past');
});

it('should set period to "next"', async () => {
const {timeframe} = await renderTimeframe({period: 'next'});
expect(timeframe.period).toBe('next');
});
});

describe('when setting unit', () => {
it('should set unit to "day"', async () => {
const {timeframe} = await renderTimeframe({unit: 'day'});
expect(timeframe.unit).toBe('day');
});

it('should set unit to "month"', async () => {
const {timeframe} = await renderTimeframe({unit: 'month'});
expect(timeframe.unit).toBe('month');
});

it('should set unit to "year"', async () => {
const {timeframe} = await renderTimeframe({unit: 'year'});
expect(timeframe.unit).toBe('year');
});
});

describe('when setting amount', () => {
it('should set amount to 1', async () => {
const {timeframe} = await renderTimeframe({amount: 1});
expect(timeframe.amount).toBe(1);
});

it('should set amount to 10', async () => {
const {timeframe} = await renderTimeframe({amount: 10});
expect(timeframe.amount).toBe(10);
});

it('should set amount to 365', async () => {
const {timeframe} = await renderTimeframe({amount: 365});
expect(timeframe.amount).toBe(365);
});
});

describe('when setting label', () => {
it('should set custom label', async () => {
const label = 'Last month';
const {timeframe} = await renderTimeframe({label});
expect(timeframe.label).toBe(label);
});

it('should set label to "Past year"', async () => {
const label = 'Past year';
const {timeframe} = await renderTimeframe({label});
expect(timeframe.label).toBe(label);
});
});

describe('Timeframe interface implementation', () => {
it('should implement all Timeframe interface properties', async () => {
const {timeframe} = await renderTimeframe({
period: 'past',
unit: 'month',
amount: 3,
label: 'Last quarter',
});

expect(timeframe).toHaveProperty('period');
expect(timeframe).toHaveProperty('unit');
expect(timeframe).toHaveProperty('amount');
expect(timeframe).toHaveProperty('label');
});
});

describe('property reflection', () => {
it('should reflect period attribute', async () => {
const {timeframe} = await renderTimeframe({period: 'next'});
expect(timeframe.getAttribute('period')).toBe('next');
});

it('should reflect unit attribute', async () => {
const {timeframe} = await renderTimeframe({unit: 'week'});
expect(timeframe.getAttribute('unit')).toBe('week');
});

it('should reflect amount attribute', async () => {
const {timeframe} = await renderTimeframe({amount: 7});
expect(timeframe.getAttribute('amount')).toBe('7');
});

it('should reflect label attribute when set', async () => {
const {timeframe} = await renderTimeframe({label: 'Custom Label'});
expect(timeframe.getAttribute('label')).toBe('Custom Label');
});
});
});
Loading
Loading