diff --git a/packages/atomic-react/src/components/search/components.ts b/packages/atomic-react/src/components/search/components.ts index 28b1f1ab923..6ac43f0e0be 100644 --- a/packages/atomic-react/src/components/search/components.ts +++ b/packages/atomic-react/src/components/search/components.ts @@ -23,6 +23,7 @@ import { AtomicResultChildrenTemplate as LitAtomicResultChildrenTemplate, AtomicResultDate as LitAtomicResultDate, AtomicResultHtml as LitAtomicResultHtml, + AtomicResultIcon as LitAtomicResultIcon, AtomicResultImage as LitAtomicResultImage, AtomicResultList as LitAtomicResultList, AtomicResultLocalizedText as LitAtomicResultLocalizedText, @@ -199,6 +200,12 @@ export const AtomicResultHtml = createComponent({ elementClass: LitAtomicResultHtml, }); +export const AtomicResultIcon = createComponent({ + tagName: 'atomic-result-icon', + react: React, + elementClass: LitAtomicResultIcon, +}); + export const AtomicResultImage = createComponent({ tagName: 'atomic-result-image', react: React, diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index 3953b8cd09d..7ac18ef29bd 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -1496,12 +1496,6 @@ export namespace Components { */ interface AtomicResultFieldsList { } - /** - * The `atomic-result-icon` component outputs the corresponding icon for a given file type. - * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. - */ - interface AtomicResultIcon { - } /** * The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item. */ @@ -2642,16 +2636,6 @@ declare global { prototype: HTMLAtomicResultFieldsListElement; new (): HTMLAtomicResultFieldsListElement; }; - /** - * The `atomic-result-icon` component outputs the corresponding icon for a given file type. - * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. - */ - interface HTMLAtomicResultIconElement extends Components.AtomicResultIcon, HTMLStencilElement { - } - var HTMLAtomicResultIconElement: { - prototype: HTMLAtomicResultIconElement; - new (): HTMLAtomicResultIconElement; - }; /** * The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item. */ @@ -3009,7 +2993,6 @@ declare global { "atomic-refine-toggle": HTMLAtomicRefineToggleElement; "atomic-result-children": HTMLAtomicResultChildrenElement; "atomic-result-fields-list": HTMLAtomicResultFieldsListElement; - "atomic-result-icon": HTMLAtomicResultIconElement; "atomic-result-link": HTMLAtomicResultLinkElement; "atomic-result-placeholder": HTMLAtomicResultPlaceholderElement; "atomic-result-printable-uri": HTMLAtomicResultPrintableUriElement; @@ -4448,12 +4431,6 @@ declare namespace LocalJSX { */ interface AtomicResultFieldsList { } - /** - * The `atomic-result-icon` component outputs the corresponding icon for a given file type. - * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. - */ - interface AtomicResultIcon { - } /** * The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item. */ @@ -4933,7 +4910,6 @@ declare namespace LocalJSX { "atomic-refine-toggle": AtomicRefineToggle; "atomic-result-children": AtomicResultChildren; "atomic-result-fields-list": AtomicResultFieldsList; - "atomic-result-icon": AtomicResultIcon; "atomic-result-link": AtomicResultLink; "atomic-result-placeholder": AtomicResultPlaceholder; "atomic-result-printable-uri": AtomicResultPrintableUri; @@ -5169,11 +5145,6 @@ declare module "@stencil/core" { * The `atomic-result-fields-list` component selectively renders its children to ensure they fit the parent element and adds dividers between them. */ "atomic-result-fields-list": LocalJSX.AtomicResultFieldsList & JSXBase.HTMLAttributes; - /** - * The `atomic-result-icon` component outputs the corresponding icon for a given file type. - * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. - */ - "atomic-result-icon": LocalJSX.AtomicResultIcon & JSXBase.HTMLAttributes; /** * The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item. */ diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.new.stories.tsx b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.new.stories.tsx similarity index 70% rename from packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.new.stories.tsx rename to packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.new.stories.tsx index 1dc54122c7a..bc1015aa4cd 100644 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.new.stories.tsx +++ b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.new.stories.tsx @@ -1,59 +1,58 @@ 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 {wrapInResultList} from '@/storybook-utils/search/result-list-wrapper'; import {wrapInResultTemplate} from '@/storybook-utils/search/result-template-wrapper'; import {wrapInSearchInterface} from '@/storybook-utils/search/search-interface-wrapper'; -const {events, args, argTypes, template} = getStorybookHelpers( - 'atomic-result-icon', - {excludeCategories: ['methods']} -); +const searchApiHarness = new MockSearchApi(); + +searchApiHarness.searchEndpoint.mock((response) => ({ + name: 'atomic-result-icon', + ...response, + results: response.results.slice(0, 1), + totalCount: 1, + totalCountFiltered: 1, +})); const {decorator: searchInterfaceDecorator, play} = wrapInSearchInterface({ - config: { - preprocessRequest: (request) => { - const parsed = JSON.parse(request.body as string); - console.log(parsed); - parsed.numberOfResults = 1; - request.body = JSON.stringify(parsed); - return request; - }, - }, + includeCodeRoot: false, }); +const {decorator: resultListDecorator} = wrapInResultList('list', false); +const {decorator: resultTemplateDecorator} = wrapInResultTemplate(); -const {decorator: resultListDecorator} = wrapInResultList('list'); -const {decorator: resultTemplateDecorator} = wrapInResultTemplate(false); +const {events, args, argTypes, template} = getStorybookHelpers( + 'atomic-result-icon', + {excludeCategories: ['methods']} +); const meta: Meta = { component: 'atomic-result-icon', - title: 'Search/ResultList/ResultIcon', + title: 'Search/Result Icon', id: 'atomic-result-icon', + render: (args) => template(args), decorators: [ - (story) => html` - - ${story()} - - `, resultTemplateDecorator, resultListDecorator, searchInterfaceDecorator, ], parameters: { ...parameters, + msw: { + handlers: [...searchApiHarness.handlers], + }, actions: { handles: events, }, }, args, argTypes, + play, }; export default meta; -export const Default: Story = { - name: 'atomic-result-icon', -}; +export const Default: Story = {}; diff --git a/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.spec.ts b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.spec.ts new file mode 100644 index 00000000000..cda7d67871b --- /dev/null +++ b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.spec.ts @@ -0,0 +1,198 @@ +import type {Result} from '@coveo/headless'; +import {html} from 'lit'; +import {beforeEach, describe, expect, it, vi} from 'vitest'; +import {page} from 'vitest/browser'; +import {renderInAtomicResult} from '@/vitest-utils/testing-helpers/fixtures/atomic/search/atomic-result-fixture'; +import {buildFakeResult} from '@/vitest-utils/testing-helpers/fixtures/headless/search/result'; +import {AtomicResultIcon} from './atomic-result-icon'; +import './atomic-result-icon'; + +vi.mock('@coveo/headless', {spy: true}); + +describe('atomic-result-icon', () => { + let mockResult: Result; + + const renderComponent = async (options: {result?: Result} = {}) => { + const resultToUse = options.result ?? mockResult; + const {element, atomicResult, atomicInterface} = + await renderInAtomicResult({ + template: html``, + selector: 'atomic-result-icon', + result: resultToUse, + bindings: (bindings) => { + bindings.engine.logger = {warn: vi.fn()} as never; + bindings.store = { + ...bindings.store, + onChange: vi.fn(), + state: { + ...bindings.store?.state, + loadingFlags: [], + iconAssetsPath: '', + }, + }; + return bindings; + }, + }); + + await atomicInterface.updateComplete; + await atomicResult.updateComplete; + await element?.updateComplete; + + return { + element, + getAtomicIcon: () => element?.shadowRoot?.querySelector('atomic-icon'), + getSlot: () => element?.shadowRoot?.querySelector('slot'), + }; + }; + + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: 'pdf', + objecttype: undefined, + urihash: '', + }, + }); + }); + + it('should be defined', () => { + const el = document.createElement('atomic-result-icon'); + expect(el).toBeInstanceOf(AtomicResultIcon); + }); + + describe('when the rendered result has a known filetype', () => { + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: 'pdf', + objecttype: undefined, + urihash: '', + }, + }); + }); + + it('should render the icon of the filetype', async () => { + const {getAtomicIcon} = await renderComponent(); + const icon = getAtomicIcon(); + + expect(icon).toBeDefined(); + expect(icon?.getAttribute('icon')).toBe('assets://pdf'); + expect(icon?.getAttribute('title')).toBe('pdf'); + }); + + it('should not render a slot', async () => { + const {getSlot} = await renderComponent(); + expect(getSlot()).toBeNull(); + }); + + it('should be accessible', async () => { + await renderComponent(); + await expect.element(page.getByTitle('pdf')).toBeInTheDocument(); + }); + }); + + describe('when the rendered result has a known objecttype', () => { + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: undefined, + objecttype: 'account', + urihash: '', + }, + }); + }); + + it('should render the icon of the objecttype', async () => { + const {getAtomicIcon} = await renderComponent(); + const icon = getAtomicIcon(); + + expect(icon).toBeDefined(); + expect(icon?.getAttribute('icon')).toBe('assets://account'); + expect(icon?.getAttribute('title')).toBe('account'); + }); + + it('should not render a slot', async () => { + const {getSlot} = await renderComponent(); + expect(getSlot()).toBeNull(); + }); + }); + + describe('when the rendered result has both known filetype and objecttype', () => { + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: 'pdf', + objecttype: 'account', + urihash: '', + }, + }); + }); + + it('should render the objecttype icon (objecttype takes precedence)', async () => { + const {getAtomicIcon} = await renderComponent(); + const icon = getAtomicIcon(); + + expect(icon).toBeDefined(); + expect(icon?.getAttribute('icon')).toBe('assets://account'); + }); + }); + + describe('when the rendered result has no known filetype or objecttype', () => { + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: 'unknowntype', + objecttype: 'anotherunknown', + urihash: '', + }, + }); + }); + + it('should render a slot with a generic document icon', async () => { + const {getSlot, getAtomicIcon} = await renderComponent(); + + // Should have a slot for fallback content + expect(getSlot()).toBeDefined(); + + // The icon should still render with the default 'document' icon + const icon = getAtomicIcon(); + expect(icon).toBeDefined(); + expect(icon?.getAttribute('icon')).toBe('assets://document'); + expect(icon?.getAttribute('title')).toBe('document'); + }); + }); + + describe('when the rendered result has undefined filetype and objecttype', () => { + beforeEach(() => { + mockResult = buildFakeResult({ + raw: { + filetype: undefined, + objecttype: undefined, + urihash: '', + }, + }); + }); + + it('should render a slot with a generic document icon', async () => { + const {getSlot, getAtomicIcon} = await renderComponent(); + + // Should have a slot for fallback content + expect(getSlot()).toBeDefined(); + + // The icon should still render with the default 'document' icon + const icon = getAtomicIcon(); + expect(icon).toBeDefined(); + expect(icon?.getAttribute('icon')).toBe('assets://document'); + }); + }); + + describe('license property', () => { + it('should have a license property', async () => { + const {element} = await renderComponent(); + + expect(element?.license).toBeDefined(); + expect(element?.license).toContain('Salesforce'); + expect(element?.license).toContain('Creative Commons'); + }); + }); +}); diff --git a/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.ts b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.ts new file mode 100644 index 00000000000..e07ac5e663b --- /dev/null +++ b/packages/atomic/src/components/search/atomic-result-icon/atomic-result-icon.ts @@ -0,0 +1,113 @@ +import type {Result} from '@coveo/headless'; +import {ResultTemplatesHelpers} from '@coveo/headless'; +import bgIcons from '@salesforce-ux/design-system/design-tokens/dist/bg-standard.common'; +import {css, html, LitElement, nothing} from 'lit'; +import {customElement, state} from 'lit/decorators.js'; +import {when} from 'lit/directives/when.js'; +import '@/src/components/common/atomic-icon/atomic-icon'; +import type {Bindings} from '@/src/components/search/atomic-search-interface/interfaces'; +import {createResultContextController} from '@/src/components/search/result-template-component-utils/context/result-context-controller'; +import {bindings} from '@/src/decorators/bindings'; +import {errorGuard} from '@/src/decorators/error-guard'; +import type {InitializableComponent} from '@/src/decorators/types'; +import {snakeToCamel} from '@/src/utils/utils'; +import {fileTypeIcons} from './file-type-icons'; +import {objectTypeIcons} from './object-type-icons'; + +/** + * The `atomic-result-icon` component outputs the corresponding icon for a given file type. + * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. + * + * @slot default - Fallback content to display when no matching icon is found. + */ +@customElement('atomic-result-icon') +@bindings() +export class AtomicResultIcon + extends LitElement + implements InitializableComponent +{ + static styles = css` + :host { + display: inline-block; + width: 100%; + height: auto; + } + + atomic-icon { + width: 100%; + } + `; + + public license = + "Salesforce's specific icons originally from lightning design system (https://www.lightningdesignsystem.com/icons/). Icons licensed under Creative Commons Attribution-NoDerivatives 4.0 (https://github.com/salesforce-ux/design-system)."; + + @state() public bindings!: Bindings; + + @state() public error!: Error; + + private resultController = createResultContextController(this); + + public initialize() {} + + private get result(): Result | null { + const item = this.resultController.item; + if (!item) { + return null; + } + if ('result' in item) { + return item.result as Result; + } + return item as Result; + } + + private get icon(): string | null { + if (!this.result) return null; + + const fileTypeValue = ResultTemplatesHelpers.getResultProperty( + this.result, + 'filetype' + ) as string; + const objectTypeValue = ResultTemplatesHelpers.getResultProperty( + this.result, + 'objecttype' + ) as string; + + const fileType = fileTypeIcons[fileTypeValue?.toLowerCase()]; + const objectType = objectTypeIcons[objectTypeValue?.toLowerCase()]; + + return objectType ?? fileType ?? null; + } + + private renderIcon() { + const icon = this.icon || 'document'; + const backgroundColor = + bgIcons[snakeToCamel(icon) as keyof typeof bgIcons] || 'transparent'; + return html` + + `; + } + + @errorGuard() + protected render() { + if (this.resultController.hasError) { + return html`${nothing}`; + } + + return when( + this.icon, + () => this.renderIcon(), + () => html`${this.renderIcon()}` + ); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-result-icon': AtomicResultIcon; + } +} diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/atomic-result-icon.e2e.ts b/packages/atomic/src/components/search/atomic-result-icon/e2e/atomic-result-icon.e2e.ts similarity index 57% rename from packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/atomic-result-icon.e2e.ts rename to packages/atomic/src/components/search/atomic-result-icon/e2e/atomic-result-icon.e2e.ts index 6791d3c5c8c..f2590f36941 100644 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/atomic-result-icon.e2e.ts +++ b/packages/atomic/src/components/search/atomic-result-icon/e2e/atomic-result-icon.e2e.ts @@ -3,9 +3,11 @@ import {expect, test} from './fixture'; test.describe('AtomicResultIcon', () => { test.beforeEach(async ({resultIcon}) => { await resultIcon.load(); + await resultIcon.hydrated.waitFor(); }); - test('atomic-result-icon loads an icon', async ({resultIcon}) => { + test('should load and display an icon', async ({resultIcon}) => { await expect(resultIcon.svg).toBeVisible(); + await expect(resultIcon.atomicIcon).toBeVisible(); }); }); diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/fixture.ts b/packages/atomic/src/components/search/atomic-result-icon/e2e/fixture.ts similarity index 100% rename from packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/fixture.ts rename to packages/atomic/src/components/search/atomic-result-icon/e2e/fixture.ts diff --git a/packages/atomic/src/components/search/atomic-result-icon/e2e/page-object.ts b/packages/atomic/src/components/search/atomic-result-icon/e2e/page-object.ts new file mode 100644 index 00000000000..1d19fb5559e --- /dev/null +++ b/packages/atomic/src/components/search/atomic-result-icon/e2e/page-object.ts @@ -0,0 +1,39 @@ +import type {Page} from '@playwright/test'; +import {BasePageObject} from '@/playwright-utils/lit-base-page-object'; + +export class ResultIconPageObject extends BasePageObject { + constructor(page: Page) { + super(page, 'atomic-result-icon'); + } + + get svg() { + return this.hydrated.locator('svg'); + } + + get atomicIcon() { + return this.hydrated.locator('atomic-icon'); + } + + /** + * Wait for component to be stable before screenshot + */ + async waitForVisualStability() { + await this.hydrated.waitFor(); + await this.page.waitForTimeout(500); + await this.page.evaluate(() => document.fonts.ready); + await this.page.waitForLoadState('networkidle'); + } + + async captureScreenshot(options?: {animations?: 'disabled' | 'allow'}) { + await this.waitForVisualStability(); + + const element = await this.hydrated.elementHandle(); + if (!element) { + throw new Error('Component element not found'); + } + + return await element.screenshot({ + animations: options?.animations ?? 'disabled', + }); + } +} diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/file-type-icons.ts b/packages/atomic/src/components/search/atomic-result-icon/file-type-icons.ts similarity index 100% rename from packages/atomic/src/components/search/result-template-components/atomic-result-icon/file-type-icons.ts rename to packages/atomic/src/components/search/atomic-result-icon/file-type-icons.ts diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/object-type-icons.ts b/packages/atomic/src/components/search/atomic-result-icon/object-type-icons.ts similarity index 100% rename from packages/atomic/src/components/search/result-template-components/atomic-result-icon/object-type-icons.ts rename to packages/atomic/src/components/search/atomic-result-icon/object-type-icons.ts diff --git a/packages/atomic/src/components/search/index.ts b/packages/atomic/src/components/search/index.ts index 3f87cacf9ed..790c20c0358 100644 --- a/packages/atomic/src/components/search/index.ts +++ b/packages/atomic/src/components/search/index.ts @@ -16,6 +16,7 @@ export {AtomicResultBadge} from './atomic-result-badge/atomic-result-badge.js'; export {AtomicResultChildrenTemplate} from './atomic-result-children-template/atomic-result-children-template.js'; export {AtomicResultDate} from './atomic-result-date/atomic-result-date.js'; export {AtomicResultHtml} from './atomic-result-html/atomic-result-html.js'; +export {AtomicResultIcon} from './atomic-result-icon/atomic-result-icon.js'; export {AtomicResultImage} from './atomic-result-image/atomic-result-image.js'; export {AtomicResultList} from './atomic-result-list/atomic-result-list.js'; export {AtomicResultLocalizedText} from './atomic-result-localized-text/atomic-result-localized-text.js'; diff --git a/packages/atomic/src/components/search/lazy-index.ts b/packages/atomic/src/components/search/lazy-index.ts index 48d9a143a72..1f1151b4383 100644 --- a/packages/atomic/src/components/search/lazy-index.ts +++ b/packages/atomic/src/components/search/lazy-index.ts @@ -32,6 +32,8 @@ export default { await import('./atomic-result-date/atomic-result-date.js'), 'atomic-result-html': async () => await import('./atomic-result-html/atomic-result-html.js'), + 'atomic-result-icon': async () => + await import('./atomic-result-icon/atomic-result-icon.js'), 'atomic-result-image': async () => await import('./atomic-result-image/atomic-result-image.js'), 'atomic-result-list': async () => diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.pcss b/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.pcss deleted file mode 100644 index c0d7cb949e4..00000000000 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.pcss +++ /dev/null @@ -1,9 +0,0 @@ -:host { - display: inline_block; - width: 100%; - height: auto; - - atomic-icon { - width: 100%; - } -} diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.spec.ts b/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.spec.ts deleted file mode 100644 index a4ab1ab7156..00000000000 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {describe, it} from 'vitest'; - -describe('AtomicResultIcon', () => { - describe('when not used inside a result template', () => { - it.todo('should remove itself'); - it.todo('should log an error'); - }); - describe('when the rendered result has a known filetype', () => { - it.todo('should render the icon of the filetype'); - it.todo('should be accessible'); - }); - describe('when the rendered result has no known filetype and a known objecttype', () => { - it.todo('should render the icon of the objecttype'); - it.todo('should be accessible'); - }); - describe('when the rendered result has no known filetype or objecttype', () => { - it.todo('should render a generic icon'); - it.todo('should be accessible'); - }); -}); diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.tsx b/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.tsx deleted file mode 100644 index 9f41eabdeb5..00000000000 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/atomic-result-icon.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import {Result, ResultTemplatesHelpers} from '@coveo/headless'; -import bgIcons from '@salesforce-ux/design-system/design-tokens/dist/bg-standard.common'; -import {Component, Element, h} from '@stencil/core'; -import {snakeToCamel} from '../../../../utils/utils'; -import {ResultContext} from '@/src/components/search/result-template-component-utils/context/stencil-result-template-decorators'; -import {fileTypeIcons} from './file-type-icons'; -import {objectTypeIcons} from './object-type-icons'; - -/** - * The `atomic-result-icon` component outputs the corresponding icon for a given file type. - * The component searches for a suitable icon, or outputs a generic icon if the search is unsuccessful. - */ -@Component({ - tag: 'atomic-result-icon', - styleUrl: 'atomic-result-icon.pcss', - shadow: true, -}) -export class AtomicResultIcon { - public license = - "Salesforce's specific icons originally from lightning design system (https://www.lightningdesignsystem.com/icons/). Icons licensed under Creative Commons Attribution-NoDerivatives 4.0 (https://github.com/salesforce-ux/design-system)."; - - @ResultContext() private result!: Result; - - @Element() host!: HTMLElement; - - private get icon() { - const fileTypeValue = ResultTemplatesHelpers.getResultProperty( - this.result, - 'filetype' - ) as string; - const objectTypeValue = ResultTemplatesHelpers.getResultProperty( - this.result, - 'objecttype' - ) as string; - - const fileType = fileTypeIcons[fileTypeValue?.toLowerCase()]; - const objectType = objectTypeIcons[objectTypeValue?.toLowerCase()]; - if (!fileType && !objectType) { - return null; - } - return objectType || fileType; - } - - private renderIcon() { - const icon = this.icon || 'document'; - const backgroundColor = bgIcons[snakeToCamel(icon)] || 'transparent'; - return ( - - ); - } - - public render() { - return this.icon ? this.renderIcon() : {this.renderIcon()}; - } -} diff --git a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/page-object.ts b/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/page-object.ts deleted file mode 100644 index 31905c735db..00000000000 --- a/packages/atomic/src/components/search/result-template-components/atomic-result-icon/e2e/page-object.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type {Page} from '@playwright/test'; -import {BasePageObject} from '@/playwright-utils/base-page-object'; - -export class ResultIconPageObject extends BasePageObject<'atomic-result-icon'> { - constructor(page: Page) { - super(page, 'atomic-result-icon'); - } - - get svg() { - return this.hydrated.locator('svg'); - } -} diff --git a/packages/atomic/src/utils/custom-element-tags.ts b/packages/atomic/src/utils/custom-element-tags.ts index 8e07e3bd3b0..2053fd79265 100644 --- a/packages/atomic/src/utils/custom-element-tags.ts +++ b/packages/atomic/src/utils/custom-element-tags.ts @@ -85,6 +85,7 @@ export const ATOMIC_CUSTOM_ELEMENT_TAGS = new Set([ 'atomic-result-children-template', 'atomic-result-date', 'atomic-result-html', + 'atomic-result-icon', 'atomic-result-image', 'atomic-result-list', 'atomic-result-localized-text', diff --git a/packages/atomic/tsconfig.lit.json b/packages/atomic/tsconfig.lit.json index 09754be637f..39c1b6d6bbd 100644 --- a/packages/atomic/tsconfig.lit.json +++ b/packages/atomic/tsconfig.lit.json @@ -18,6 +18,7 @@ "src/components/index.ts", "src/components/lazy-index.ts", "src/components.d.ts", - "src/lit-components.d.ts" + "src/lit-components.d.ts", + "src/icons.d.ts" ] }