diff --git a/packages/atomic-react/src/components/commerce/components.ts b/packages/atomic-react/src/components/commerce/components.ts index 802560bc747..6bcfecff5f8 100644 --- a/packages/atomic-react/src/components/commerce/components.ts +++ b/packages/atomic-react/src/components/commerce/components.ts @@ -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'; @@ -387,3 +388,9 @@ export const AtomicProductText = createComponent({ react: React, elementClass: LitAtomicProductText, }); + +export const AtomicTimeframe = createComponent({ + tagName: 'atomic-timeframe', + react: React, + elementClass: LitAtomicTimeframe, +}); diff --git a/packages/atomic-react/src/components/search/components.ts b/packages/atomic-react/src/components/search/components.ts index fe7e1327468..85b0c771246 100644 --- a/packages/atomic-react/src/components/search/components.ts +++ b/packages/atomic-react/src/components/search/components.ts @@ -75,6 +75,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'; @@ -534,3 +535,9 @@ export const AtomicText = createComponent({ react: React, elementClass: LitAtomicText, }); + +export const AtomicTimeframe = createComponent({ + tagName: 'atomic-timeframe', + react: React, + elementClass: LitAtomicTimeframe, +}); diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index c2d8036026d..e5cc3f9f364 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -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, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, RelativeDateUnit, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless"; +import { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, 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"; @@ -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, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, RelativeDateUnit, Result, ResultTemplate, ResultTemplateCondition } from "@coveo/headless"; +export { DateFilterRange, DateRangeRequest, FacetResultsMustMatch, 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"; @@ -1132,28 +1132,6 @@ export namespace Components { */ "select": () => 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. @@ -1956,16 +1934,6 @@ declare global { prototype: HTMLAtomicTabButtonElement; new (): HTMLAtomicTabButtonElement; }; - /** - * 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. @@ -2040,7 +2008,6 @@ declare global { "atomic-stencil-facet-date-input": HTMLAtomicStencilFacetDateInputElement; "atomic-suggestion-renderer": HTMLAtomicSuggestionRendererElement; "atomic-tab-button": HTMLAtomicTabButtonElement; - "atomic-timeframe": HTMLAtomicTimeframeElement; "atomic-timeframe-facet": HTMLAtomicTimeframeFacetElement; } } @@ -3125,28 +3092,6 @@ declare namespace LocalJSX { */ "select": () => 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. @@ -3273,7 +3218,6 @@ declare namespace LocalJSX { "atomic-stencil-facet-date-input": AtomicStencilFacetDateInput; "atomic-suggestion-renderer": AtomicSuggestionRenderer; "atomic-tab-button": AtomicTabButton; - "atomic-timeframe": AtomicTimeframe; "atomic-timeframe-facet": AtomicTimeframeFacet; } } @@ -3453,11 +3397,6 @@ declare module "@stencil/core" { */ "atomic-suggestion-renderer": LocalJSX.AtomicSuggestionRenderer & JSXBase.HTMLAttributes; "atomic-tab-button": LocalJSX.AtomicTabButton & JSXBase.HTMLAttributes; - /** - * 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; /** * 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. diff --git a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.mdx b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.mdx new file mode 100644 index 00000000000..58bfd0900c1 --- /dev/null +++ b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.mdx @@ -0,0 +1,39 @@ +import { Meta } from '@storybook/addon-docs/blocks'; +import * as AtomicTimeframeStories from './atomic-timeframe.new.stories'; +import { AtomicDocTemplate } from '@/storybook-utils/documentation/atomic-doc-template'; + + + + + + +The `atomic-timeframe` component defines a relative date range for filtering results in a timeframe facet. It allows users to filter by periods like "past week", "next month", or other custom time ranges. + +This component must be placed as a child of `atomic-timeframe-facet` (for search interfaces) or `atomic-insight-timeframe-facet` (for insight panels). + +```html + + ... + + ... + + ... + + + + + + +``` + +## Related Components + +- [atomic-timeframe-facet](../?path=/docs/atomic-timeframe-facet--default) - Search timeframe facet +- [atomic-insight-timeframe-facet](../?path=/docs/atomic-insight-timeframe-facet--default) - Insight timeframe facet + + diff --git a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.new.stories.tsx b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.new.stories.tsx index a791c41c3f5..78debb0601c 100644 --- a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.new.stories.tsx +++ b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.new.stories.tsx @@ -1,10 +1,14 @@ import type {Meta, StoryObj as Story} from '@storybook/web-components-vite'; import {getStorybookHelpers} from '@wc-toolkit/storybook-helpers'; import {html} from 'lit'; +import {MockSearchApi} from '@/storybook-utils/api/search/mock'; import {parameters} from '@/storybook-utils/common/common-meta-parameters'; import {wrapInSearchInterface} from '@/storybook-utils/search/search-interface-wrapper'; -const {decorator, play} = wrapInSearchInterface(); +const searchApiHarness = new MockSearchApi(); +const {decorator, play} = wrapInSearchInterface({ + includeCodeRoot: false, +}); const {events, args, argTypes, template} = getStorybookHelpers( 'atomic-timeframe', {excludeCategories: ['methods']} @@ -15,28 +19,220 @@ const meta: Meta = { title: 'Common/Timeframe', id: 'atomic-timeframe', - render: (args) => template(args), + render: (args) => html` + + ${template(args)} + + `, decorators: [decorator], parameters: { ...parameters, + msw: { + handlers: [...searchApiHarness.handlers], + }, actions: { handles: events, }, }, - args, - argTypes, - + args: { + ...args, + unit: 'week', + amount: 1, + period: 'past', + }, + argTypes: { + ...argTypes, + unit: { + control: {type: 'select'}, + options: ['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'], + }, + amount: { + control: {type: 'number', min: 1}, + }, + }, + beforeEach: async () => { + searchApiHarness.searchEndpoint.clear(); + }, play, }; export default meta; export const Default: Story = { - name: 'atomic-timeframe', - args: {unit: 'year'}, - decorators: [ - (story) => html` - ${story()} - `, - ], + args: { + period: 'past', + unit: 'week', + amount: 1, + }, + beforeEach: async () => { + const now = Date.now(); + + searchApiHarness.searchEndpoint.mockOnce((response) => { + if (!('results' in response)) return response; + return { + ...response, + // biome-ignore lint/suspicious/noExplicitAny: Mock response type needs flexibility + results: response.results.slice(0, 10).map((r: any, i: number) => ({ + ...r, + raw: { + ...r.raw, + date: now - i * 24 * 60 * 60 * 1000, // Past dates: last 10 days + }, + })), + facets: [ + ...response.facets, + { + facetId: 'date', + field: 'date', + moreValuesAvailable: false, + values: [ + { + start: 'past-1-week', + end: 'now', + endInclusive: false, + state: 'idle', + numberOfResults: 7, + }, + ], + }, + ], + }; + }); + }, +}; + +export const WithPastPeriod: Story = { + name: 'Past Timeframe', + args: { + period: 'past', + unit: 'month', + amount: 3, + }, + beforeEach: async () => { + const now = Date.now(); + const oneMonth = 30 * 24 * 60 * 60 * 1000; + + searchApiHarness.searchEndpoint.mockOnce((response) => { + if (!('results' in response)) return response; + return { + ...response, + // biome-ignore lint/suspicious/noExplicitAny: Mock response type needs flexibility + results: response.results.slice(0, 10).map((r: any, i: number) => ({ + ...r, + raw: { + ...r.raw, + date: now - i * oneMonth * 0.3, // Spread over last 3 months + }, + })), + facets: [ + ...response.facets, + { + facetId: 'date', + field: 'date', + moreValuesAvailable: false, + values: [ + { + start: 'past-3-month', + end: 'now', + endInclusive: false, + state: 'idle', + numberOfResults: 42, + }, + ], + }, + ], + }; + }); + }, +}; + +export const WithNextPeriod: Story = { + name: 'Next Timeframe', + args: { + period: 'next', + unit: 'year', + amount: 1, + }, + beforeEach: async () => { + const now = Date.now(); + const oneYear = 365 * 24 * 60 * 60 * 1000; + + searchApiHarness.searchEndpoint.mockOnce((response) => { + if (!('results' in response)) return response; + return { + ...response, + // biome-ignore lint/suspicious/noExplicitAny: Mock response type needs flexibility + results: response.results.slice(0, 10).map((r: any, i: number) => ({ + ...r, + raw: { + ...r.raw, + date: now + (i + 1) * oneYear, // Future dates: 1-10 years from now + }, + })), + facets: [ + ...response.facets, + { + facetId: 'date', + field: 'date', + moreValuesAvailable: false, + values: [ + { + start: 'now', + end: 'next-1-year', + endInclusive: false, + state: 'idle', + numberOfResults: 5, + }, + ], + }, + ], + }; + }); + }, +}; + +export const WithCustomLabel: Story = { + name: 'With Custom Label', + args: { + period: 'past', + unit: 'month', + amount: 6, + label: 'Last Semester', + }, + beforeEach: async () => { + const now = Date.now(); + const oneMonth = 30 * 24 * 60 * 60 * 1000; + + searchApiHarness.searchEndpoint.mockOnce((response) => { + if (!('results' in response)) return response; + return { + ...response, + // biome-ignore lint/suspicious/noExplicitAny: Mock response type needs flexibility + results: response.results.slice(0, 10).map((r: any, i: number) => ({ + ...r, + raw: { + ...r.raw, + date: now - i * oneMonth * 0.6, // Spread over last 6 months + }, + })), + facets: [ + ...response.facets, + { + facetId: 'date', + field: 'date', + moreValuesAvailable: false, + values: [ + { + start: 'past-6-month', + end: 'now', + endInclusive: false, + state: 'idle', + numberOfResults: 28, + }, + ], + }, + ], + }; + }); + }, }; diff --git a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.spec.ts b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.spec.ts new file mode 100644 index 00000000000..5e6a217e8de --- /dev/null +++ b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.spec.ts @@ -0,0 +1,190 @@ +import {html} from 'lit'; +import {describe, expect, it, vi} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import type {AtomicTimeframe} from './atomic-timeframe'; +import './atomic-timeframe'; +import type {RelativeDateUnit} from '@coveo/headless'; + +describe('atomic-timeframe', () => { + const renderTimeframe = async ({ + period = 'past', + unit = 'day', + amount = 1, + label, + }: { + period?: 'past' | 'next'; + unit?: RelativeDateUnit; + amount?: number; + label?: string; + } = {}) => { + const element = await renderFunctionFixture(html` + + `); + + const timeframe = element.querySelector( + 'atomic-timeframe' + ) as AtomicTimeframe; + + return {element, timeframe}; + }; + + 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); + }); + + 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'); + }); + + it.each([ + 'minute', + 'hour', + 'day', + 'week', + 'month', + 'quarter', + 'year', + ])('should set unit to "%s"', async (unit) => { + const {timeframe} = await renderTimeframe({unit}); + expect(timeframe.unit).toBe(unit); + }); + + it('should set amount to a valid value', async () => { + const {timeframe} = await renderTimeframe({amount: 10}); + expect(timeframe.amount).toBe(10); + }); + + it('should set custom label', async () => { + const label = 'Last month'; + const {timeframe} = await renderTimeframe({label}); + expect(timeframe.label).toBe(label); + }); + + 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'); + }); + + // TODO V4: KIT-5197 - Remove this test + it.each<{ + prop: 'period' | 'unit' | 'amount'; + validValue: string | number; + invalidValue: string | number; + }>([ + { + prop: 'period', + validValue: 'past', + invalidValue: 'invalid', + }, + { + prop: 'unit', + validValue: 'day', + invalidValue: 'invalid', + }, + { + prop: 'amount', + validValue: 1, + invalidValue: 0, + }, + ])( + 'should log validation warning when #$prop is updated to invalid value', + async ({prop, validValue, invalidValue}) => { + const consoleWarnSpy = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}); + + const {timeframe} = await renderTimeframe({[prop]: validValue}); + + // biome-ignore lint/suspicious/noExplicitAny: testing invalid values + (timeframe as any)[prop] = invalidValue; + await timeframe.updateComplete; + + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Prop validation failed for component atomic-timeframe' + ), + timeframe + ); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining(prop), + timeframe + ); + + consoleWarnSpy.mockRestore(); + } + ); + + // TODO V4: KIT-5197 - Remove skip + it.skip.each<{ + prop: 'period' | 'unit' | 'amount'; + validValue: string | number; + invalidValue: string | number; + }>([ + { + prop: 'period', + validValue: 'past', + invalidValue: 'invalid', + }, + { + prop: 'unit', + validValue: 'day', + invalidValue: 'invalid', + }, + { + prop: 'amount', + validValue: 1, + invalidValue: 0, + }, + { + prop: 'amount', + validValue: 1, + invalidValue: -1, + }, + ])( + 'should set error when valid #$prop is updated to an invalid value', + async ({prop, validValue, invalidValue}) => { + const {timeframe} = await renderTimeframe({[prop]: validValue}); + + expect(timeframe.error).toBeUndefined(); + + // biome-ignore lint/suspicious/noExplicitAny: testing invalid values + (timeframe as any)[prop] = invalidValue; + await timeframe.updateComplete; + + expect(timeframe.error).toBeDefined(); + expect(timeframe.error.message).toMatch(new RegExp(prop, 'i')); + } + ); +}); diff --git a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.ts b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.ts new file mode 100644 index 00000000000..fdabb784b30 --- /dev/null +++ b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.ts @@ -0,0 +1,81 @@ +import {NumberValue, Schema, StringValue} from '@coveo/bueno'; +import type {RelativeDateUnit} from '@coveo/headless'; +import {LitElement} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import type {Timeframe} from '@/src/components/common/facets/timeframe-facet-common'; +import {ValidatePropsController} from '@/src/components/common/validate-props-controller/validate-props-controller'; +import type {LitElementWithError} from '@/src/decorators/types'; +import {LightDomMixin} from '@/src/mixins/light-dom'; + +/** + * The `atomic-timeframe` component defines a timeframe for an `atomic-timeframe-facet`. + * This component must be a child of an `atomic-timeframe-facet` component. + * + * A timeframe represents a span of time relative to the current moment, either in the past or future. + */ +@customElement('atomic-timeframe') +export class AtomicTimeframe + extends LightDomMixin(LitElement) + implements Timeframe, LitElementWithError +{ + private static readonly propsSchema = new Schema({ + period: new StringValue({ + constrainTo: ['past', 'next'], + required: false, + }), + unit: new StringValue({ + constrainTo: [ + 'minute', + 'hour', + 'day', + 'week', + 'month', + 'quarter', + 'year', + ], + required: true, + emptyAllowed: false, + }), + amount: new NumberValue({min: 1, required: false}), + }); + /** + * The direction of time relative to the current moment. + */ + @property({type: String, reflect: true}) public period: 'past' | 'next' = + 'past'; + + /** + * The unit of time for the timeframe (e.g., "day", "week", "month"). + */ + @property({type: String, reflect: true}) public unit!: RelativeDateUnit; + + /** + * The number of time units for the timeframe. + */ + @property({type: Number, reflect: true}) public amount = 1; + + /** + * A custom, non-localized label for the timeframe. + * When specified, this label appears instead of the auto-generated timeframe description. + */ + @property({type: String, reflect: true}) public label?: string; + + @state() public error!: Error; + + constructor() { + super(); + + new ValidatePropsController( + this, + () => ({period: this.period, unit: this.unit, amount: this.amount}), + AtomicTimeframe.propsSchema, + false + ); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-timeframe': AtomicTimeframe; + } +} diff --git a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.tsx b/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.tsx deleted file mode 100644 index 9412860b257..00000000000 --- a/packages/atomic/src/components/common/atomic-timeframe/atomic-timeframe.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import {RelativeDateUnit} from '@coveo/headless'; -import {Component, Prop} from '@stencil/core'; -import {Timeframe} from '../facets/stencil-timeframe-facet-common'; - - -/** - * 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. - */ -@Component({ - tag: 'atomic-timeframe', - shadow: false, -}) -export class AtomicTimeframe implements Timeframe { - /** - * The relative period of time to now. - */ - @Prop({reflect: true}) public period: 'past' | 'next' = 'past'; - - /** - * 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` - */ - @Prop({reflect: true}) public unit!: RelativeDateUnit; - - /** - * The amount of units from which to count. - * - * For example, 10 days, 1 year, etc. - */ - @Prop({reflect: true}) public amount = 1; - - /** - * 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. - */ - @Prop({reflect: true}) public label?: string; -} diff --git a/packages/atomic/src/components/common/index.ts b/packages/atomic/src/components/common/index.ts index c8c7776da25..2d4a453cd27 100644 --- a/packages/atomic/src/components/common/index.ts +++ b/packages/atomic/src/components/common/index.ts @@ -10,3 +10,4 @@ export {AtomicModal} from './atomic-modal/atomic-modal.js'; export {AtomicNumericRange} from './atomic-numeric-range/atomic-numeric-range.js'; export {AtomicTabBar} from './atomic-tab-bar/atomic-tab-bar.js'; export {AtomicTabPopover} from './atomic-tab-popover/atomic-tab-popover.js'; +export {AtomicTimeframe} from './atomic-timeframe/atomic-timeframe.js'; diff --git a/packages/atomic/src/components/common/lazy-index.ts b/packages/atomic/src/components/common/lazy-index.ts index c3be65d3740..43369f06052 100644 --- a/packages/atomic/src/components/common/lazy-index.ts +++ b/packages/atomic/src/components/common/lazy-index.ts @@ -20,6 +20,8 @@ export default { await import('./atomic-tab-bar/atomic-tab-bar.js'), 'atomic-tab-popover': async () => await import('./atomic-tab-popover/atomic-tab-popover.js'), + 'atomic-timeframe': async () => + await import('./atomic-timeframe/atomic-timeframe.js'), } as Record Promise>; export type * from './index.js'; diff --git a/packages/atomic/src/utils/custom-element-tags.ts b/packages/atomic/src/utils/custom-element-tags.ts index 86f1c3a4679..c3bb3a4cae3 100644 --- a/packages/atomic/src/utils/custom-element-tags.ts +++ b/packages/atomic/src/utils/custom-element-tags.ts @@ -145,6 +145,7 @@ export const ATOMIC_CUSTOM_ELEMENT_TAGS = new Set([ 'atomic-tab-popover', 'atomic-table-element', 'atomic-text', + 'atomic-timeframe', ]); /**