Skip to content

Commit

Permalink
[sitecore-jss-react] [sitecore-jss-nextjs] Default Placeholder Conten…
Browse files Browse the repository at this point in the history
…t for empty fields in editMode metadata (#1831)

* default empty field editing placeholder for text field component

* add withEmptyPlaceholderValue HOC; unit tests; default empty field editing components for text and image; modify Link and Text field components

* modify Date, Image, RichText react components to use withEmptyValueEditingPlaceholder

* update Link, NextImage, RichText nextjs fields components to work with withEmptyValueEditingPlaceholder

* add proptype for custom empty value placeholder component prop

* added comments, fixed lint errors

* fix lint errors

* reexport default empty field editing components and withEmptyValueEditingPlaceholder from sitecore-jss-nextjs package

* renamings HOC, properties - withEmptyFieldEditingComponent

* update hasValue logic

* react field components - extract common properties into EditableFieldProps interface

* introduce a function fieldValueIsEmpty in base packages and use it in withEmptyFieldEditingComponent

* fix lint error

* fix lint errors

* introduced FieldMetadata interface in core package; use it accross field components; some renamings and additional code comments

* update withEmptyEditingComponent hoc to accept single options parameter

* add unit tests for isValueEmpty; isValueEmpty update

* isFieldValueEmpty - account for boolean value; add additional code comments, update some existing comments

* isEmptyField refactoring and unit tests update

* unit tests update

* update emptyFieldComponent prop proptype; use isFieldValueEmpty in field components; update Image and Link unit tests

* update nextjs Link and NextImage unit tests, small updates

* some test title updates

* add additional check for metadata for isEditing var in next richtext

* add clarifying comment
  • Loading branch information
yavorsk authored Jul 8, 2024
1 parent 4125328 commit b4309e8
Show file tree
Hide file tree
Showing 27 changed files with 1,888 additions and 490 deletions.
187 changes: 162 additions & 25 deletions packages/sitecore-jss-nextjs/src/components/Link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { mount } from 'enzyme';
import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime';
import { Link } from './Link';
import { spy } from 'sinon';
import { describe } from 'node:test';

const Router = (): NextRouter => ({
pathname: '/',
Expand Down Expand Up @@ -361,7 +362,7 @@ describe('<Link />', () => {
expect(rendered).to.have.length(0);
});

it('should render field metadata component when metadata property is present', () => {
describe('editMode metadata', () => {
const testMetadata = {
contextItem: {
id: '{09A07660-6834-476C-B93B-584248D3003B}',
Expand All @@ -374,29 +375,165 @@ describe('<Link />', () => {
rawValue: 'Test1',
};

const field = {
value: {
href: '/lorem',
text: 'ipsum',
class: 'my-link',
},
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<a href="/lorem" class="my-link">ipsum</a>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
it('should render field metadata component when metadata property is present', () => {
const field = {
value: {
href: '/lorem',
text: 'ipsum',
class: 'my-link',
},
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<a href="/lorem" class="my-link">ipsum</a>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render default empty field component when field value href is not present', () => {
const field = {
value: {
href: undefined,
},
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span>[No text in field]</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render default empty field component when field href is not present', () => {
const field = {
href: undefined,
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span>[No text in field]</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render custom empty field component when provided, when field value href is not present', () => {
const field = {
value: {
href: undefined,
},
metadata: testMetadata,
};

const EmptyFieldEditingComponent: React.FC = () => (
<span className="empty-field-value-placeholder">Custom Empty field value</span>
);

const rendered = mount(
<Page>
<Link field={field} emptyFieldEditingComponent={EmptyFieldEditingComponent} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span class="empty-field-value-placeholder">Custom Empty field value</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render custom empty field component when provided, when field href is not present', () => {
const field = {
href: undefined,
metadata: testMetadata,
};

const EmptyFieldEditingComponent: React.FC = () => (
<span className="empty-field-value-placeholder">Custom Empty field value</span>
);

const rendered = mount(
<Page>
<Link field={field} emptyFieldEditingComponent={EmptyFieldEditingComponent} />
</Page>
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span class="empty-field-value-placeholder">Custom Empty field value</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render nothing when field value href is not present and editing is explicitly disabled', () => {
const field = {
value: { href: undefined },
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} editable={false} />
</Page>
);

expect(rendered.html()).to.equal('');
});

it('should render nothing when field href is not present and editing is explicitly disabled', () => {
const field = {
href: undefined,
metadata: testMetadata,
};

const rendered = mount(
<Page>
<Link field={field} editable={false} />
</Page>
);

expect(rendered.html()).to.equal('');
});
});
});
8 changes: 6 additions & 2 deletions packages/sitecore-jss-nextjs/src/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(

if (
!field ||
(!(field as LinkFieldValue).editable && !field.value && !(field as LinkFieldValue).href)
(!(field as LinkFieldValue).editable &&
!field.value &&
!(field as LinkFieldValue).href &&
!field.metadata)
) {
return null;
}

const value = ((field as LinkFieldValue).href
? field
: (field as LinkField).value) as LinkFieldValue;
const { href, querystring, anchor } = value;
// fallback to {} if value is undefined; could happen if field is LinkFieldValue, href is empty in metadata mode
const { href, querystring, anchor } = value || {};

const isEditing =
editable && ((field as LinkFieldValue).editable || (field as LinkFieldValue).metadata);
Expand Down
149 changes: 132 additions & 17 deletions packages/sitecore-jss-nextjs/src/components/NextImage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import chaiString from 'chai-string';
import { mount } from 'enzyme';
import React from 'react';
import { NextImage } from './NextImage';
import { ImageField } from '@sitecore-jss/sitecore-jss-react';
import {
ImageField,
DefaultEmptyFieldEditingComponentImage,
} from '@sitecore-jss/sitecore-jss-react';
import { ImageLoader } from 'next/image';
import { spy, match } from 'sinon';
import sinonChai from 'sinon-chai';
Expand Down Expand Up @@ -287,7 +290,7 @@ describe('<NextImage />', () => {
});
});

it('should render field metadata component when metadata property is present', () => {
describe('editMode metadata', () => {
const testMetadata = {
contextItem: {
id: '{09A07660-6834-476C-B93B-584248D3003B}',
Expand All @@ -300,21 +303,133 @@ describe('<NextImage />', () => {
rawValue: 'Test1',
};

const field = {
value: { src: '/assets/img/test0.png', alt: 'my image' },
metadata: testMetadata,
};
it('should render field metadata component when metadata property is present', () => {
const field = {
value: { src: '/assets/img/test0.png', alt: 'my image' },
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} fill={true} />);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<img alt="my image" loading="lazy" decoding="async" data-nimg="fill" style="position: absolute; height: 100%; width: 100%; left: 0px; top: 0px; right: 0px; bottom: 0px; color: transparent;" sizes="100vw" srcset="/_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=3840&amp;q=75">',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render default empty field component for Image when field value src is not present', () => {
const field = {
value: {},
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} />);
const defaultEmptyImagePlaceholder = mount(<DefaultEmptyFieldEditingComponentImage />);
expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
defaultEmptyImagePlaceholder.html(),
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render default empty field component for Image when field src is not present', () => {
const field = {
src: undefined,
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} />);
const defaultEmptyImagePlaceholder = mount(<DefaultEmptyFieldEditingComponentImage />);
expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
defaultEmptyImagePlaceholder.html(),
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render custom empty field component when provided, when field value src is not present', () => {
const field = {
value: {},
metadata: testMetadata,
};

const EmptyFieldEditingComponent: React.FC = () => (
<span className="empty-field-value-placeholder">Custom Empty field value</span>
);

const rendered = mount(
<NextImage field={field} emptyFieldEditingComponent={EmptyFieldEditingComponent} />
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span class="empty-field-value-placeholder">Custom Empty field value</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render custom empty field component when provided, when field src is not present', () => {
const field = {
src: undefined,
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} fill={true} />);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<img alt="my image" loading="lazy" decoding="async" data-nimg="fill" style="position: absolute; height: 100%; width: 100%; left: 0px; top: 0px; right: 0px; bottom: 0px; color: transparent;" sizes="100vw" srcset="/_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fassets%2Fimg%2Ftest0.png&amp;w=3840&amp;q=75">',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
const EmptyFieldEditingComponent: React.FC = () => (
<span className="empty-field-value-placeholder">Custom Empty field value</span>
);

const rendered = mount(
<NextImage field={field} emptyFieldEditingComponent={EmptyFieldEditingComponent} />
);

expect(rendered.html()).to.equal(
[
`<code type="text/sitecore" chrometype="field" class="scpm" kind="open">${JSON.stringify(
testMetadata
)}</code>`,
'<span class="empty-field-value-placeholder">Custom Empty field value</span>',
'<code type="text/sitecore" chrometype="field" class="scpm" kind="close"></code>',
].join('')
);
});

it('should render nothing when field value is not present, when editing is explicitly disabled', () => {
const field = {
value: {},
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} editable={false} />);

expect(rendered.html()).to.equal('');
});

it('should render nothing when field src is not present, when editing is explicitly disabled', () => {
const field = {
src: undefined,
metadata: testMetadata,
};

const rendered = mount(<NextImage field={field} editable={false} />);

expect(rendered.html()).to.equal('');
});
});
});
Loading

0 comments on commit b4309e8

Please sign in to comment.