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 alt text field on atomic-result-image #4056

Merged
merged 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -1226,14 +1226,14 @@ export declare interface AtomicResultIcon extends Components.AtomicResultIcon {}


@ProxyCmp({
inputs: ['fallback', 'field']
inputs: ['fallback', 'field', 'imageAltField']
})
@Component({
selector: 'atomic-result-image',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['fallback', 'field'],
inputs: ['fallback', 'field', 'imageAltField'],
})
export class AtomicResultImage {
protected el: HTMLElement;
Expand Down
12 changes: 10 additions & 2 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1890,7 +1890,7 @@ export namespace Components {
/**
* The product field that contains the alt text for the images. This will look for the field in the product object first, then in the product.additionalFields object. The field can be a string or an array of strings. If the value of the field is a string, it will be used as the alt text for all the images. If the value of the field is an array of strings, the alt text will be used in the order of the images. If the field is not specified, or does not contain a valid value, the alt text will be set to "Image {index} out of {totalImages} for {productName}".
*/
"imagesAltField"?: string;
"imageAltField"?: string;
/**
* Navigates to the specified image index.
* @param index - The index of the image to navigate to.
Expand Down Expand Up @@ -2547,6 +2547,10 @@ export namespace Components {
* The result field which the component should use. This will look for the field in the Result object first, then in the Result.raw object. It is important to include the necessary field in the `atomic-search-interface` component.
*/
"field": string;
/**
* The result field that contains the alt text for the image. This will look for the field in the Result object first, then in the Result.raw object If the field is not specified, or does not contain a valid value, the alt text will be set to "Image for {productName}".
*/
"imageAltField"?: string;
}
/**
* The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item.
Expand Down Expand Up @@ -7234,7 +7238,7 @@ declare namespace LocalJSX {
/**
* The product field that contains the alt text for the images. This will look for the field in the product object first, then in the product.additionalFields object. The field can be a string or an array of strings. If the value of the field is a string, it will be used as the alt text for all the images. If the value of the field is an array of strings, the alt text will be used in the order of the images. If the field is not specified, or does not contain a valid value, the alt text will be set to "Image {index} out of {totalImages} for {productName}".
*/
"imagesAltField"?: string;
"imageAltField"?: string;
}
interface AtomicProductLink {
/**
Expand Down Expand Up @@ -7842,6 +7846,10 @@ declare namespace LocalJSX {
* The result field which the component should use. This will look for the field in the Result object first, then in the Result.raw object. It is important to include the necessary field in the `atomic-search-interface` component.
*/
"field": string;
/**
* The result field that contains the alt text for the image. This will look for the field in the Result object first, then in the Result.raw object If the field is not specified, or does not contain a valid value, the alt text will be set to "Image for {productName}".
*/
"imageAltField"?: string;
}
/**
* The `atomic-result-link` component automatically transforms a search result title into a clickable link that points to the original item.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class AtomicProductImage implements InitializableComponent {
*
* If the field is not specified, or does not contain a valid value, the alt text will be set to "Image {index} out of {totalImages} for {productName}".
*/
@Prop({reflect: true}) imagesAltField?: string;
@Prop({reflect: true}) imageAltField?: string;

/**
* An fallback image URL that will be used in case the specified image is not available or an error is encountered.
Expand Down Expand Up @@ -128,23 +128,23 @@ export class AtomicProductImage implements InitializableComponent {
(image) => typeof image === 'string'
);

const validImagesAlt = this.imagesAlt;
const validImageAlt = this.imageAlt;

this.images = validImages.map((url, index) => {
const finalUrl = this.useFallback ? this.fallback : url;

this.validateUrl(finalUrl);
let altText;

if (Array.isArray(validImagesAlt) && validImagesAlt[index]) {
altText = validImagesAlt[index];
} else if (typeof validImagesAlt === 'string') {
altText = validImagesAlt;
if (Array.isArray(validImageAlt) && validImageAlt[index]) {
altText = validImageAlt[index];
} else if (typeof validImageAlt === 'string') {
altText = validImageAlt;
} else {
altText = this.bindings.i18n.t('image-alt-fallback', {
altText = this.bindings.i18n.t('image-alt-fallback-multiple', {
count: index + 1,
max: validImages?.length,
productName: this.product.ec_name,
itemName: this.product.ec_name,
});
}

Expand All @@ -164,11 +164,11 @@ export class AtomicProductImage implements InitializableComponent {
return Array.isArray(value) ? value : [value];
}

private get imagesAlt() {
if (this.imagesAltField) {
private get imageAlt() {
if (this.imageAltField) {
const value = ProductTemplatesHelpers.getProductProperty(
this.product,
this.imagesAltField
this.imageAltField
);

if (Array.isArray(value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export class AtomicResultImage implements InitializableComponent {
*/
@Prop({reflect: true}) field!: string;

/**
* The result field that contains the alt text for the image. This will look for the field in the Result object first, then in the Result.raw object
*
* If the field is not specified, or does not contain a valid value, the alt text will be set to "Image for {productName}".
*/
@Prop({reflect: true}) imageAltField?: string;
fpbrault marked this conversation as resolved.
Show resolved Hide resolved

/**
* An optional fallback image URL that will be used in case the specified image field is not available or encounters an error.
*/
Expand All @@ -42,6 +49,27 @@ export class AtomicResultImage implements InitializableComponent {
return Array.isArray(value) ? value[0] : value;
}

private get altText(): string {
if (this.imageAltField) {
const value = ResultTemplatesHelpers.getResultProperty(
this.result,
this.field
);

if (Array.isArray(value) && typeof value[0] === 'string') {
return value[0];
}

if (typeof value === 'string') {
return value;
}
}

return this.bindings.i18n.t('image-alt-fallback', {
itemName: this.result.title,
});
}

private logWarning(message: string) {
this.bindings.engine.logger.warn(message, this.host);
}
Expand Down Expand Up @@ -87,7 +115,7 @@ export class AtomicResultImage implements InitializableComponent {

return (
<img
alt={`${this.field} image`}
alt={this.altText}
src={filterProtocol(url)}
onError={() => this.handleImageError()}
loading="lazy"
Expand Down
77 changes: 52 additions & 25 deletions packages/atomic/src/locales.json
Original file line number Diff line number Diff line change
Expand Up @@ -7426,32 +7426,59 @@
"zh-CN": "可用于:",
"zh-TW": "可用於:"
},
"image-alt-fallback-multiple": {
"en": "Image {{count}} out of {{max}} for {{itemName}}",
"fr": "Image {{count}} sur {{max}} pour {{itemName}}",
"cs": "Obrázek {{count}} z {{max}} pro {{itemName}}",
"da": "Billede {{count}} ud af {{max}} for {{itemName}}",
"de": "Bild {{count}} von {{max}} für {{itemName}}",
"el": "Εικόνα {{count}} από {{max}} για {{itemName}}",
"es": "Imagen {{count}} de {{max}} para {{itemName}}",
"fi": "Kuva {{count}} / {{max}} tuotteelle {{itemName}}",
"hu": "Kép {{count}} a {{max}}-ból a(z) {{itemName}} termékhez",
"id": "Gambar {{count}} dari {{max}} untuk {{itemName}}",
"it": "Immagine {{count}} su {{max}} per {{itemName}}",
"ja": "{{max}}のうちのイメージ{{count}}、{{itemName}}",
"ko": "{{max}}개 중 이미지 {{count}} for {{itemName}}",
"nl": "Afbeelding {{count}} van {{max}} voor {{itemName}}",
"no": "Bilde {{count}} av {{max}} for {{itemName}}",
"pl": "Obraz {{count}} z {{max}} dla {{itemName}}",
"pt": "Imagem {{count}} de {{max}} para {{itemName}}",
"pt-BR": "Imagem {{count}} de {{max}} para {{itemName}}",
"ru": "Изображение {{count}} из {{max}} для {{itemName}}",
"sv": "Bild {{count}} av {{max}} för {{itemName}}",
"th": "รูปภาพ {{count}} จาก {{max}} สำหรับ {{itemName}}",
"tr": "{{max}} üzerinden Resim {{count}} için {{itemName}}",
"zh": "第{{count}}张图片,共{{max}}张,适用于{{itemName}}",
"zh-CN": "第{{count}}张图片,共{{max}}张,适用于{{itemName}}",
"zh-TW": "第{{count}}張圖片,共{{max}}張,適用於{{itemName}}"
},
"image-alt-fallback": {
"en": "Image {{count}} out of {{max}} for {{productName}}",
"fr": "Image {{count}} sur {{max}} pour {{productName}}",
"cs": "Obrázek {{count}} z {{max}} pro {{productName}}",
"da": "Billede {{count}} ud af {{max}} for {{productName}}",
"de": "Bild {{count}} von {{max}} für {{productName}}",
"el": "Εικόνα {{count}} από {{max}} για {{productName}}",
"es": "Imagen {{count}} de {{max}} para {{productName}}",
"fi": "Kuva {{count}} / {{max}} tuotteelle {{productName}}",
"hu": "Kép {{count}} a {{max}}-ból a(z) {{productName}} termékhez",
"id": "Gambar {{count}} dari {{max}} untuk {{productName}}",
"it": "Immagine {{count}} su {{max}} per {{productName}}",
"ja": "{{max}}のうちのイメージ{{count}}、{{productName}}",
"ko": "{{max}}개 중 이미지 {{count}} for {{productName}}",
"nl": "Afbeelding {{count}} van {{max}} voor {{productName}}",
"no": "Bilde {{count}} av {{max}} for {{productName}}",
"pl": "Obraz {{count}} z {{max}} dla {{productName}}",
"pt": "Imagem {{count}} de {{max}} para {{productName}}",
"pt-BR": "Imagem {{count}} de {{max}} para {{productName}}",
"ru": "Изображение {{count}} из {{max}} для {{productName}}",
"sv": "Bild {{count}} av {{max}} för {{productName}}",
"th": "รูปภาพ {{count}} จาก {{max}} สำหรับ {{productName}}",
"tr": "{{max}} üzerinden Resim {{count}} için {{productName}}",
"zh": "第{{count}}张图片,共{{max}}张,适用于{{productName}}",
"zh-CN": "第{{count}}张图片,共{{max}}张,适用于{{productName}}",
"zh-TW": "第{{count}}張圖片,共{{max}}張,適用於{{productName}}"
"en": "Image for {{itemName}}",
"fr": "Image pour {{itemName}}",
"cs": "Obrázek pro {{itemName}}",
"da": "Billede for {{itemName}}",
"de": "Bild für {{itemName}}",
"el": "Εικόνα για {{itemName}}",
"es": "Imagen para {{itemName}}",
"fi": "Kuva tuotteelle {{itemName}}",
"hu": "Kép a(z) {{itemName}} termékhez",
"id": "Gambar untuk {{itemName}}",
"it": "Immagine per {{itemName}}",
"ja": "{{itemName}}の画像",
"ko": "{{itemName}}의 이미지",
"nl": "Afbeelding voor {{itemName}}",
"no": "Bilde for {{itemName}}",
"pl": "Obraz dla {{itemName}}",
"pt": "Imagem para {{itemName}}",
"pt-BR": "Imagem para {{itemName}}",
"ru": "Изображение для {{itemName}}",
"sv": "Bild för {{itemName}}",
"th": "รูปภาพสำหรับ {{itemName}}",
"tr": "{{itemName}} için resim",
"zh": "{{itemName}}的图片",
"zh-CN": "{{itemName}}的图片",
"zh-TW": "{{itemName}}的圖片"
},
"image-not-found-alt": {
"en": "No image available.",
Expand Down
Loading