diff --git a/apps/ai-image-tagging/frontend/.prettierrc.js b/apps/ai-image-tagging/frontend/.prettierrc.js
index 5b03dfcea58..13d14c76333 100644
--- a/apps/ai-image-tagging/frontend/.prettierrc.js
+++ b/apps/ai-image-tagging/frontend/.prettierrc.js
@@ -3,5 +3,5 @@ module.exports = {
tabWidth: 2,
useTabs: false,
singleQuote: true,
- jsxBracketSameLine: true
+ jsxBracketSameLine: true,
};
diff --git a/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.js b/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.js
index 6c633081379..3c30d2d08ea 100644
--- a/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.js
+++ b/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.js
@@ -1,6 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { CheckboxField, TextInput, Pill, Button, Note } from '@contentful/forma-36-react-components';
+import {
+ CheckboxField,
+ TextInput,
+ Pill,
+ Button,
+ Note,
+} from '@contentful/forma-36-react-components';
import get from 'lodash.get';
import { styles } from './styles';
@@ -12,26 +18,25 @@ async function callAPI(url) {
}
export class AITagView extends React.Component {
-
static propTypes = {
entries: PropTypes.object.isRequired,
notifier: PropTypes.object.isRequired,
locale: PropTypes.string.isRequired,
- space: PropTypes.object.isRequired
+ space: PropTypes.object.isRequired,
};
- constructor(props){
+ constructor(props) {
super(props);
this.state = {
- value: "",
+ value: '',
tags: props.entries.imageTags.getValue() || [],
overwrite: true,
isMissingImage: !props.entries.image.getValue(),
unsupportedImageType: false,
imageRequirementsNotMet: false,
- isFetchingTags: false
- }
+ isFetchingTags: false,
+ };
this.validateImage();
}
@@ -39,7 +44,7 @@ export class AITagView extends React.Component {
componentDidMount() {
this.props.entries.image.onValueChanged(() => {
this.setState(() => ({
- isMissingImage: !this.props.entries.image.getValue()
+ isMissingImage: !this.props.entries.image.getValue(),
}));
// always validate the image
this.validateImage();
@@ -50,7 +55,9 @@ export class AITagView extends React.Component {
const MAX_FILE_SIZE = 5 * Math.pow(2, 20); // 5MB limit from AWS Rekognition
const MIN_DIMENSION_SIZE = 80; // 80px limit
const imageId = get(this.props.entries.image.getValue(), 'sys.id');
- if (!imageId) { return ; }
+ if (!imageId) {
+ return;
+ }
const file = await this.props.space.getAsset(imageId);
const locale = this.props.locale;
@@ -58,46 +65,49 @@ export class AITagView extends React.Component {
const details = get(file, `fields.file.${locale}.details`);
// test if file extension is PNG/JPEG/JPG
const isImageTypeValid = new RegExp(/^image\/(png|jpe?g)$/, 'i').test(contentType);
- const isImageIncompatible = details.size > MAX_FILE_SIZE ||
- details.width < MIN_DIMENSION_SIZE ||
- details.height < MIN_DIMENSION_SIZE;
+ const isImageIncompatible =
+ details.size > MAX_FILE_SIZE ||
+ details.width < MIN_DIMENSION_SIZE ||
+ details.height < MIN_DIMENSION_SIZE;
this.setState(() => ({
unsupportedImageType: !isImageTypeValid,
- imageRequirementsNotMet: isImageIncompatible
- }))
- }
+ imageRequirementsNotMet: isImageIncompatible,
+ }));
+ };
toggleOverwrite = () => {
- this.setState(state => ({
- overwrite: !state.overwrite
+ this.setState((state) => ({
+ overwrite: !state.overwrite,
}));
- }
+ };
updateTags = async (tags) => {
const tagsWithoutDuplicates = [...new Set(tags)];
await this.props.entries.imageTags.setValue(tagsWithoutDuplicates);
this.setState(() => ({
- tags: tagsWithoutDuplicates
+ tags: tagsWithoutDuplicates,
}));
- }
+ };
addTag = async (e) => {
- if (e.key !== "Enter" || !e.target.value) { return; }
+ if (e.key !== 'Enter' || !e.target.value) {
+ return;
+ }
await this.updateTags([e.target.value, ...this.state.tags]);
this.setState(() => ({
- value: ""
+ value: '',
}));
- }
+ };
deleteTag = async (tag) => {
- const newTags = this.state.tags.filter(t => t !== tag);
+ const newTags = this.state.tags.filter((t) => t !== tag);
await this.updateTags(newTags);
- }
+ };
- fetchTags = async () => {
- const imageId = get(this.props.entries.image.getValue(), 'sys.id')
+ fetchTags = async () => {
+ const imageId = get(this.props.entries.image.getValue(), 'sys.id');
const file = await this.props.space.getAsset(imageId);
const locale = this.props.locale;
const fullURL = get(file, `fields.file.${locale}.url`);
@@ -110,67 +120,72 @@ export class AITagView extends React.Component {
// upload new tags
const newTags = this.state.overwrite ? aiTags : [...aiTags, ...this.state.tags];
this.updateTags(newTags);
- } catch(e) {
- this.props.notifier.error("Image Tagging failed. Please try again later.")
+ } catch (e) {
+ this.props.notifier.error('Image Tagging failed. Please try again later.');
} finally {
this.setState(() => ({ isFetchingTags: false }));
}
- }
+ };
updateValue = (e) => {
this.setState({
- value: e.target.value
+ value: e.target.value,
});
- }
+ };
render() {
- let hasImageError = !this.state.isMissingImage && (this.state.unsupportedImageType || this.state.imageRequirementsNotMet)
- let imageErrorMsg = this.state.unsupportedImageType ? "Unfortunately, we can only auto-tag PNG and JPG file types" : "Please make sure your image is less than 5MB and has dimensions of at least 80px for both width and height";
-
- return
-
-
- {
- this.state.tags.map((tag, index) => (
+ let hasImageError =
+ !this.state.isMissingImage &&
+ (this.state.unsupportedImageType || this.state.imageRequirementsNotMet);
+ let imageErrorMsg = this.state.unsupportedImageType
+ ? 'Unfortunately, we can only auto-tag PNG and JPG file types'
+ : 'Please make sure your image is less than 5MB and has dimensions of at least 80px for both width and height';
+
+ return (
+
+
+
+ {this.state.tags.map((tag, index) => (
- ))
- }
+ ))}
+
+ {hasImageError && (
+
+ {imageErrorMsg}
+
+ )}
+
+ Auto-tag from AI
+
+
- {
- hasImageError &&
-
{ imageErrorMsg }
- }
-
- Auto-tag from AI
-
-
-
+ );
}
}
diff --git a/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.spec.js b/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.spec.js
index c5d82ead958..4800297fc17 100644
--- a/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.spec.js
+++ b/apps/ai-image-tagging/frontend/src/components/AITagView/AITagView.spec.js
@@ -11,30 +11,36 @@ const sdk = {
fields: {
image: {
getValue: jest.fn(),
- onValueChanged: jest.fn()
+ onValueChanged: jest.fn(),
},
imageTags: {
getValue: jest.fn(),
- setValue: jest.fn()
- }
- }
+ setValue: jest.fn(),
+ },
+ },
},
space: {
- getAsset: jest.fn()
+ getAsset: jest.fn(),
},
notifier: {
- error: jest.fn()
- }
+ error: jest.fn(),
+ },
};
configure({ testIdAttribute: 'data-test-id' });
function renderComponent(sdk) {
- return render(
)
+ return render(
+
+ );
}
describe('AITagView', () => {
-
describe('if there is no image', () => {
it('should disable everything', async () => {
const appView = renderComponent(sdk);
@@ -45,25 +51,24 @@ describe('AITagView', () => {
expect(getByTestId('image-tag').disabled).toBeTruthy();
expect(appView.container).toMatchSnapshot();
});
- })
+ });
describe('if there is an image', () => {
beforeEach(() => {
const imgData = {
- url: "//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png",
+ url: '//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png',
contentType: 'image/png',
- details: { size: 200, height: 100, width: 100 }
+ details: { size: 200, height: 100, width: 100 },
};
sdk.space.getAsset.mockImplementation(() => ({
- fields: {file: { 'en-US': imgData }}
- }))
-
- })
+ fields: { file: { 'en-US': imgData } },
+ }));
+ });
it('should enable everything', async () => {
sdk.entry.fields.image.getValue.mockImplementation(() => ({
sys: {
- id: '098dsjnwe9ds'
- }
+ id: '098dsjnwe9ds',
+ },
}));
const { getByTestId, getByText, container } = renderComponent(sdk);
@@ -78,8 +83,8 @@ describe('AITagView', () => {
const tags = ['tag1', 'tag2'];
sdk.entry.fields.image.getValue.mockImplementation(() => ({
sys: {
- id: '098dsjnwe9ds'
- }
+ id: '098dsjnwe9ds',
+ },
}));
sdk.entry.fields.imageTags.getValue.mockImplementation(() => tags);
@@ -91,8 +96,8 @@ describe('AITagView', () => {
it('should add image tags on Enter', async () => {
sdk.entry.fields.image.getValue.mockImplementation(() => ({
sys: {
- id: '098dsjnwe9ds'
- }
+ id: '098dsjnwe9ds',
+ },
}));
sdk.entry.fields.imageTags.getValue.mockImplementation(() => []);
@@ -101,7 +106,7 @@ describe('AITagView', () => {
await waitFor(() => getByTestId('image-tag'));
const tagInput = getByTestId('image-tag');
- fireEvent.change(tagInput, {target: { value: 'new tag'} });
+ fireEvent.change(tagInput, { target: { value: 'new tag' } });
fireEvent.keyPress(tagInput, { key: 'Enter', keyCode: 13 });
await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(1));
@@ -111,8 +116,8 @@ describe('AITagView', () => {
it('should ignore duplicate image tags', async () => {
sdk.entry.fields.image.getValue.mockImplementation(() => ({
sys: {
- id: '098dsjnwe9ds'
- }
+ id: '098dsjnwe9ds',
+ },
}));
sdk.entry.fields.imageTags.getValue.mockImplementation(() => ['tag1', 'tag2']);
@@ -121,36 +126,34 @@ describe('AITagView', () => {
await waitFor(() => getByTestId('image-tag'));
const tagInput = getByTestId('image-tag');
- fireEvent.change(tagInput, {target: { value: 'tag1'} });
+ fireEvent.change(tagInput, { target: { value: 'tag1' } });
fireEvent.keyPress(tagInput, { key: 'Enter', keyCode: 13 });
await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(2));
expect(appView.container).toMatchSnapshot();
});
- })
-
-
-
+ });
describe('Calling AI Tags', () => {
beforeEach(() => {
const imgData = {
url: '//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.jpeg',
contentType: 'image/png',
- details: { size: 200, height: 100, width: 100 }
+ details: { size: 200, height: 100, width: 100 },
};
- const expectedPath = '/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.jpeg'
+ const expectedPath =
+ '/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.jpeg';
sdk.entry.fields.image.getValue.mockImplementation(() => ({
sys: {
- id: '098dsjnwe9ds'
- }
+ id: '098dsjnwe9ds',
+ },
}));
sdk.space.getAsset.mockImplementation(() => ({
- fields: {file: { 'en-US': imgData }}
- }))
+ fields: { file: { 'en-US': imgData } },
+ }));
sdk.entry.fields.imageTags.getValue.mockImplementation(() => []);
- fetchMock.get(`/tags/${expectedPath}`, {tags: ['ai-tag-1', 'ai-tag-2', 'ai-tag-3']})
+ fetchMock.get(`/tags/${expectedPath}`, { tags: ['ai-tag-1', 'ai-tag-2', 'ai-tag-3'] });
});
afterEach(fetchMock.reset);
@@ -158,12 +161,12 @@ describe('AITagView', () => {
it('should fetch tags and render them on btn click', async () => {
const appView = renderComponent(sdk);
const { getByTestId, getAllByTestId } = appView;
- await waitFor(() => getByTestId('image-tag'))
+ await waitFor(() => getByTestId('image-tag'));
getByTestId('image-tag').value = 'new tag';
fireEvent.click(getByTestId('cf-ui-button'));
- await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(3))
+ await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(3));
expect(appView.container).toMatchSnapshot();
});
@@ -172,12 +175,12 @@ describe('AITagView', () => {
sdk.entry.fields.imageTags.getValue.mockImplementation(() => ['prior-tag']);
const appView = renderComponent(sdk);
const { getByTestId, getAllByTestId } = appView;
- await waitFor(() => getAllByTestId('cf-ui-pill'))
+ await waitFor(() => getAllByTestId('cf-ui-pill'));
expect(getAllByTestId('cf-ui-pill')).toHaveLength(1);
getByTestId('image-tag').value = 'new tag';
fireEvent.click(getByTestId('cf-ui-button'));
- await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(3))
+ await waitFor(() => expect(getAllByTestId('cf-ui-pill')).toHaveLength(3));
expect(appView.container).toMatchSnapshot();
});
@@ -200,13 +203,13 @@ describe('AITagView', () => {
it('should disable btn if image type is unsupported', async () => {
const imgData = {
- url: "//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.gif",
+ url: '//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.gif',
contentType: 'image/gif',
- details: { size: 200, height: 100, width: 100 }
+ details: { size: 200, height: 100, width: 100 },
};
sdk.space.getAsset.mockImplementation(() => ({
- fields: {file: { 'en-US': imgData }}
- }))
+ fields: { file: { 'en-US': imgData } },
+ }));
const appView = renderComponent(sdk);
const { getByTestId } = appView;
await waitFor(() => expect(getByTestId('cf-ui-button').disabled).toBeTruthy());
@@ -217,13 +220,13 @@ describe('AITagView', () => {
it('should disable btn if image dimensions are invalid', async () => {
const imgData = {
- url: "//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png",
+ url: '//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png',
contentType: 'image/png',
- details: { size: 2000, height: 70, width: 200 }
+ details: { size: 2000, height: 70, width: 200 },
};
sdk.space.getAsset.mockImplementation(() => ({
- fields: {file: { 'en-US': imgData }}
- }))
+ fields: { file: { 'en-US': imgData } },
+ }));
const appView = renderComponent(sdk);
const { getByTestId } = appView;
@@ -234,13 +237,13 @@ describe('AITagView', () => {
it('should disable btn if image is too big', async () => {
const imgData = {
- url: "//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png",
+ url: '//images.ctfassets.net/k3tebg1cbyuz/4dgP2U7BeMuk0icguS4qGw/59b8fe25285cdd1b5fcc69bd5555b3be/doge.png',
contentType: 'image/png',
- details: { size: 6000000, height: 3000, width: 300 }
+ details: { size: 6000000, height: 3000, width: 300 },
};
sdk.space.getAsset.mockImplementation(() => ({
- fields: {file: { 'en-US': imgData }}
- }))
+ fields: { file: { 'en-US': imgData } },
+ }));
const appView = renderComponent(sdk);
const { getByTestId } = appView;
diff --git a/apps/ai-image-tagging/frontend/src/components/AITagView/styles.js b/apps/ai-image-tagging/frontend/src/components/AITagView/styles.js
index 40f2e8715c4..dd9f7667b43 100644
--- a/apps/ai-image-tagging/frontend/src/components/AITagView/styles.js
+++ b/apps/ai-image-tagging/frontend/src/components/AITagView/styles.js
@@ -3,21 +3,21 @@ import tokens from '@contentful/forma-36-tokens';
export const styles = {
inputWrapper: css({
- marginTop: tokens.spacingXs
+ marginTop: tokens.spacingXs,
}),
pillWrapper: css({
marginTop: tokens.spacingXs,
- marginBottom: tokens.spacingL
+ marginBottom: tokens.spacingL,
}),
pill: css({
marginRight: tokens.spacingS,
- marginTop: tokens.spacingXs
+ marginTop: tokens.spacingXs,
}),
btn: css({
- marginRight: tokens.spacingM
+ marginRight: tokens.spacingM,
}),
fileWarning: css({
marginTop: tokens.spacingM,
- marginBottom: tokens.spacingM
- })
-}
+ marginBottom: tokens.spacingM,
+ }),
+};
diff --git a/apps/ai-image-tagging/frontend/src/components/AppView/AppView.js b/apps/ai-image-tagging/frontend/src/components/AppView/AppView.js
index e17c5ae6a67..1d2d99b413b 100644
--- a/apps/ai-image-tagging/frontend/src/components/AppView/AppView.js
+++ b/apps/ai-image-tagging/frontend/src/components/AppView/AppView.js
@@ -12,7 +12,7 @@ import appLogo from './app-logo.svg';
const makeContentType = (contentTypeId, contentTypeName) => ({
sys: {
- id: contentTypeId
+ id: contentTypeId,
},
name: contentTypeName,
displayField: 'title',
@@ -21,28 +21,28 @@ const makeContentType = (contentTypeId, contentTypeName) => ({
id: 'title',
name: 'Title',
required: true,
- type: 'Symbol'
+ type: 'Symbol',
},
{
id: 'image',
name: 'Image',
required: true,
type: 'Link',
- linkType: 'Asset'
+ linkType: 'Asset',
},
{
id: 'imageTags',
name: 'Image tags',
required: true,
type: 'Array',
- items: { type: 'Symbol' }
- }
- ]
+ items: { type: 'Symbol' },
+ },
+ ],
});
export class AppView extends Component {
static propTypes = {
- sdk: PropTypes.object.isRequired
+ sdk: PropTypes.object.isRequired,
};
state = {
@@ -50,7 +50,7 @@ export class AppView extends Component {
allContentTypesIds: [],
contentTypeId: 'imageWithAiTags',
contentTypeName: 'Image with AI tags',
- isContentTypeIdPristine: true
+ isContentTypeIdPristine: true,
};
async componentDidMount() {
@@ -58,7 +58,7 @@ export class AppView extends Component {
const [isInstalled, allContentTypes] = await Promise.all([
app.isInstalled(),
- space.getContentTypes()
+ space.getContentTypes(),
]);
const allContentTypesIds = allContentTypes.items.map(({ sys: { id } }) => id);
@@ -68,7 +68,7 @@ export class AppView extends Component {
this.setState({ isInstalled, allContentTypesIds }, () => app.setReady());
app.onConfigure(this.installApp);
- app.onConfigurationCompleted(err => {
+ app.onConfigurationCompleted((err) => {
if (!err) {
this.setState({ isInstalled: true });
}
@@ -118,23 +118,23 @@ export class AppView extends Component {
targetState: {
EditorInterface: {
[contentType.sys.id]: {
- controls: [{ fieldId: 'imageTags' }]
- }
- }
- }
+ controls: [{ fieldId: 'imageTags' }],
+ },
+ },
+ },
};
};
onContentTypeNameChange = ({ target: { value } }) =>
- this.setState(oldState => ({
+ this.setState((oldState) => ({
...(oldState.isContentTypeIdPristine && { contentTypeId: camelCase(value) }),
- contentTypeName: value
+ contentTypeName: value,
}));
onContentTypeIdChange = ({ target: { value } }) =>
this.setState({
isContentTypeIdPristine: false,
- contentTypeId: value
+ contentTypeId: value,
});
render() {
diff --git a/apps/ai-image-tagging/frontend/src/components/AppView/AppView.spec.js b/apps/ai-image-tagging/frontend/src/components/AppView/AppView.spec.js
index 73e6e4c4cb0..2b79d094071 100644
--- a/apps/ai-image-tagging/frontend/src/components/AppView/AppView.spec.js
+++ b/apps/ai-image-tagging/frontend/src/components/AppView/AppView.spec.js
@@ -6,25 +6,24 @@ import { AppView } from './AppView';
configure({ testIdAttribute: 'data-test-id' });
-
describe('AppView', () => {
- let props
+ let props;
beforeEach(() => {
props = {
sdk: {
...mockProps.sdk,
space: {
- getContentTypes: jest.fn(() => Promise.resolve({ items: [] }))
+ getContentTypes: jest.fn(() => Promise.resolve({ items: [] })),
},
app: {
setReady: jest.fn(),
isInstalled: jest.fn(),
onConfigure: jest.fn(),
- onConfigurationCompleted: jest.fn()
- }
- }
+ onConfigurationCompleted: jest.fn(),
+ },
+ },
};
- })
+ });
describe('when the app is not installed', () => {
beforeEach(() => props.sdk.app.isInstalled.mockImplementation(() => Promise.resolve(false)));
@@ -36,9 +35,11 @@ describe('AppView', () => {
});
it('should render inline validation if the content type id is taken', async () => {
- props.sdk.space.getContentTypes.mockImplementation(() => Promise.resolve({
- items: [{ sys: { id: 'imageWithFocalPoint' } }]
- }));
+ props.sdk.space.getContentTypes.mockImplementation(() =>
+ Promise.resolve({
+ items: [{ sys: { id: 'imageWithFocalPoint' } }],
+ })
+ );
const { getByTestId, getByText } = render(
);
await waitFor(() => getByText('Configuration'));
expect(getByTestId('content-type-name')).toMatchSnapshot();
diff --git a/apps/ai-image-tagging/frontend/src/components/AppView/ConfigurationContent.js b/apps/ai-image-tagging/frontend/src/components/AppView/ConfigurationContent.js
index 224f9b62987..72e3dca0faf 100644
--- a/apps/ai-image-tagging/frontend/src/components/AppView/ConfigurationContent.js
+++ b/apps/ai-image-tagging/frontend/src/components/AppView/ConfigurationContent.js
@@ -10,8 +10,8 @@ export const ConfigurationContent = () => (
Go to the "Content" page
- Create a new entry of type "AI Image Tagging" (or the name you chose during
- the installation)
+ Create a new entry of type "AI Image Tagging" (or the name you chose during the
+ installation)
Fill in the required fields and publish
@@ -21,9 +21,7 @@ export const ConfigurationContent = () => (
Go to the "content model" page
-
- Edit the content type that needs to reference the image with the AI tags
-
+ Edit the content type that needs to reference the image with the AI tags
Create a new field of type "reference"
Set a validation rule requiring that the content type the reference points to is of type
diff --git a/apps/ai-image-tagging/frontend/src/components/AppView/InstallationContent.js b/apps/ai-image-tagging/frontend/src/components/AppView/InstallationContent.js
index 7a77ff1c7c5..a7721403548 100644
--- a/apps/ai-image-tagging/frontend/src/components/AppView/InstallationContent.js
+++ b/apps/ai-image-tagging/frontend/src/components/AppView/InstallationContent.js
@@ -8,7 +8,7 @@ export function InstallationContent({
contentTypeId,
contentTypeName,
onContentTypeNameChange,
- onContentTypeIdChange
+ onContentTypeIdChange,
}) {
const validationMessageId = allContentTypesIds.includes(contentTypeId)
? `A content type with ID "${contentTypeId}" already exists. Try a different ID.`
@@ -27,7 +27,7 @@ export function InstallationContent({
name="contentTypeName"
textInputProps={{
placeholder: 'e.g. AI Tagged Image',
- testId: 'content-type-name-input'
+ testId: 'content-type-name-input',
}}
helpText="You can use this content type to add tags to images"
value={contentTypeName}
@@ -58,5 +58,5 @@ InstallationContent.propTypes = {
contentTypeId: PropTypes.string.isRequired,
contentTypeName: PropTypes.string.isRequired,
onContentTypeNameChange: PropTypes.func.isRequired,
- onContentTypeIdChange: PropTypes.func.isRequired
+ onContentTypeIdChange: PropTypes.func.isRequired,
};
diff --git a/apps/ai-image-tagging/frontend/src/components/AppView/styles.js b/apps/ai-image-tagging/frontend/src/components/AppView/styles.js
index 1d0a9dab1e4..29cfc0098d1 100644
--- a/apps/ai-image-tagging/frontend/src/components/AppView/styles.js
+++ b/apps/ai-image-tagging/frontend/src/components/AppView/styles.js
@@ -12,7 +12,7 @@ export const styles = {
backgroundColor: tokens.colorWhite,
zIndex: '2',
boxShadow: '0px 0px 20px rgba(0, 0, 0, 0.1)',
- borderRadius: '2px'
+ borderRadius: '2px',
}),
background: css({
display: 'block',
@@ -21,15 +21,15 @@ export const styles = {
top: '0',
width: '100%',
height: '300px',
- backgroundColor: tokens.orange500 // corresponds to logo
+ backgroundColor: tokens.orange500, // corresponds to logo
}),
featuresListItem: css({
listStyleType: 'disc',
- marginLeft: tokens.spacingM
+ marginLeft: tokens.spacingM,
}),
light: css({
color: tokens.gray600,
- marginTop: tokens.spacingM
+ marginTop: tokens.spacingM,
}),
logo: css({
width: '100%',
@@ -38,19 +38,19 @@ export const styles = {
marginTop: tokens.spacingXl,
marginBottom: tokens.spacingXl,
'& > img': css({
- width: '80px'
- })
+ width: '80px',
+ }),
}),
paragraph: css({
- marginTop: tokens.spacingM
+ marginTop: tokens.spacingM,
}),
heading: css({
- marginBottom: tokens.spacingM
+ marginBottom: tokens.spacingM,
}),
input: css({
- marginTop: tokens.spacingM
+ marginTop: tokens.spacingM,
}),
list: css({
- marginTop: tokens.spacingXs
- })
+ marginTop: tokens.spacingXs,
+ }),
};
diff --git a/apps/ai-image-tagging/frontend/src/components/Divider/styles.js b/apps/ai-image-tagging/frontend/src/components/Divider/styles.js
index 432a9788a14..60af7dee00a 100644
--- a/apps/ai-image-tagging/frontend/src/components/Divider/styles.js
+++ b/apps/ai-image-tagging/frontend/src/components/Divider/styles.js
@@ -7,6 +7,6 @@ export const styles = {
marginBottom: tokens.spacingL,
border: 0,
height: '1px',
- backgroundColor: tokens.gray300
- })
+ backgroundColor: tokens.gray300,
+ }),
};
diff --git a/apps/ai-image-tagging/frontend/src/index.js b/apps/ai-image-tagging/frontend/src/index.js
index 0b2e53d99b1..1bb8402a1eb 100644
--- a/apps/ai-image-tagging/frontend/src/index.js
+++ b/apps/ai-image-tagging/frontend/src/index.js
@@ -7,7 +7,7 @@ import './index.css';
import { AppView } from './components/AppView';
import { AITagView } from './components/AITagView';
-init(sdk => {
+init((sdk) => {
if (sdk.location.is(locations.LOCATION_APP_CONFIG)) {
render( , document.getElementById('root'));
}
@@ -19,8 +19,8 @@ init(sdk => {
,
document.getElementById('root')
);
diff --git a/apps/ai-image-tagging/frontend/src/test/mockProps.js b/apps/ai-image-tagging/frontend/src/test/mockProps.js
index b91d5d9117c..910d2f0d97c 100644
--- a/apps/ai-image-tagging/frontend/src/test/mockProps.js
+++ b/apps/ai-image-tagging/frontend/src/test/mockProps.js
@@ -7,8 +7,8 @@ export default {
extension: 'ai-image-tagging',
space: 'cyu19ucaypb9',
environment: 'master',
- user: '2userId252'
+ user: '2userId252',
},
- window: {}
- }
+ window: {},
+ },
};
diff --git a/apps/ai-image-tagging/lambda/app.js b/apps/ai-image-tagging/lambda/app.js
index 92c562152d9..e1fed026828 100644
--- a/apps/ai-image-tagging/lambda/app.js
+++ b/apps/ai-image-tagging/lambda/app.js
@@ -14,7 +14,7 @@ const documentClient = new AWS.DynamoDB.DocumentClient();
const deps = {
fetch,
rekog,
- documentClient
+ documentClient,
};
const app = express();
diff --git a/apps/ai-image-tagging/lambda/handler.js b/apps/ai-image-tagging/lambda/handler.js
index c96185d6ea6..5d417b5cba2 100644
--- a/apps/ai-image-tagging/lambda/handler.js
+++ b/apps/ai-image-tagging/lambda/handler.js
@@ -7,7 +7,7 @@ module.exports = async (method, path, { fetch, rekog, documentClient }) => {
if (method !== 'GET') {
return {
status: 405,
- body: { message: 'Method not allowed.' }
+ body: { message: 'Method not allowed.' },
};
}
@@ -26,7 +26,7 @@ module.exports = async (method, path, { fetch, rekog, documentClient }) => {
console.error(`Hard usage limit exceeded for space ${spaceId}. Aborting.`);
return {
status: 403,
- message: 'Usage exceeded.'
+ message: 'Usage exceeded.',
};
}
} catch (err) {
@@ -37,12 +37,12 @@ module.exports = async (method, path, { fetch, rekog, documentClient }) => {
try {
return {
status: 200,
- body: { tags: await tag(path, { fetch, rekog }) }
+ body: { tags: await tag(path, { fetch, rekog }) },
};
} catch (err) {
return {
status: 400,
- body: { message: err.message || err.errorMessage }
+ body: { message: err.message || err.errorMessage },
};
}
};
diff --git a/apps/ai-image-tagging/lambda/handler.test.js b/apps/ai-image-tagging/lambda/handler.test.js
index fd6445bd5d3..91d05f67fd0 100644
--- a/apps/ai-image-tagging/lambda/handler.test.js
+++ b/apps/ai-image-tagging/lambda/handler.test.js
@@ -15,11 +15,9 @@ describe('handler', () => {
const { status, body } = await handle('GET', '/some-space/some-image', mocks);
expect(status).toBe(200);
- expect(body).toEqual({ tags: ['cat', 'yolo']});
+ expect(body).toEqual({ tags: ['cat', 'yolo'] });
- expect(mocks.fetch).toBeCalledWith(
- 'https://images.ctfassets.net/some-space/some-image'
- );
+ expect(mocks.fetch).toBeCalledWith('https://images.ctfassets.net/some-space/some-image');
expect(mocks.documentClient.update).toBeCalledTimes(1);
expect(mocks.rekog.detectLabels).toBeCalledTimes(1);
});
diff --git a/apps/ai-image-tagging/lambda/mocks.js b/apps/ai-image-tagging/lambda/mocks.js
index 8928da06515..50cc34294e3 100644
--- a/apps/ai-image-tagging/lambda/mocks.js
+++ b/apps/ai-image-tagging/lambda/mocks.js
@@ -2,32 +2,29 @@
const fetch = jest.fn().mockResolvedValue({
status: 200,
- arrayBuffer: jest.fn().mockResolvedValue('SOME_ARR_BUFF')
+ arrayBuffer: jest.fn().mockResolvedValue('SOME_ARR_BUFF'),
});
const documentClient = {
update: jest.fn().mockReturnValue({
promise: jest.fn().mockResolvedValue({
Attributes: {
- reqs: 7
- }
- })
- })
+ reqs: 7,
+ },
+ }),
+ }),
};
const rekog = {
detectLabels: jest.fn().mockReturnValue({
promise: jest.fn().mockResolvedValue({
- Labels: [
- { Name: 'cat' },
- { Name: 'yolo' }
- ]
- })
- })
+ Labels: [{ Name: 'cat' }, { Name: 'yolo' }],
+ }),
+ }),
};
module.exports = {
fetch,
documentClient,
- rekog
+ rekog,
};
diff --git a/apps/ai-image-tagging/lambda/tag.js b/apps/ai-image-tagging/lambda/tag.js
index 70336a1639a..e8b116e0eb7 100644
--- a/apps/ai-image-tagging/lambda/tag.js
+++ b/apps/ai-image-tagging/lambda/tag.js
@@ -12,10 +12,10 @@ const fetchImage = async (imageUrl, fetch) => {
}
};
-const getDetectParams = imageData => ({
- Image: { Bytes: imageData, },
+const getDetectParams = (imageData) => ({
+ Image: { Bytes: imageData },
MaxLabels: 10,
- MinConfidence: 70.0
+ MinConfidence: 70.0,
});
module.exports = async (path, { fetch, rekog }) => {
@@ -23,5 +23,5 @@ module.exports = async (path, { fetch, rekog }) => {
const params = getDetectParams(imageData);
const tags = await rekog.detectLabels(params).promise();
- return tags.Labels.map(label => label.Name);
+ return tags.Labels.map((label) => label.Name);
};
diff --git a/apps/ai-image-tagging/lambda/tag.test.js b/apps/ai-image-tagging/lambda/tag.test.js
index ae4cc44fa03..1d7fc2f5ada 100644
--- a/apps/ai-image-tagging/lambda/tag.test.js
+++ b/apps/ai-image-tagging/lambda/tag.test.js
@@ -14,10 +14,10 @@ describe('tagging', () => {
expect(rekog.detectLabels).toBeCalledWith({
Image: {
- Bytes: 'SOME_ARR_BUFF'
+ Bytes: 'SOME_ARR_BUFF',
},
MaxLabels: 10,
- MinConfidence: 70.0
+ MinConfidence: 70.0,
});
});
diff --git a/apps/ai-image-tagging/lambda/usage.js b/apps/ai-image-tagging/lambda/usage.js
index 45dd3c42be8..53391b2358c 100644
--- a/apps/ai-image-tagging/lambda/usage.js
+++ b/apps/ai-image-tagging/lambda/usage.js
@@ -5,14 +5,16 @@ const { DateTime } = require('luxon');
module.exports = async (spaceId, documentClient) => {
const period = DateTime.utc().startOf('month').toSeconds();
- const { Attributes } = await documentClient.update({
- TableName: process.env.TABLE_NAME,
- Key: { period, spaceId },
- UpdateExpression: 'SET #attr = if_not_exists(#attr, :zero) + :incr',
- ExpressionAttributeNames: { '#attr': 'reqs' },
- ExpressionAttributeValues: { ':incr': 1, ':zero': 0 },
- ReturnValues: 'ALL_NEW'
- }).promise();
+ const { Attributes } = await documentClient
+ .update({
+ TableName: process.env.TABLE_NAME,
+ Key: { period, spaceId },
+ UpdateExpression: 'SET #attr = if_not_exists(#attr, :zero) + :incr',
+ ExpressionAttributeNames: { '#attr': 'reqs' },
+ ExpressionAttributeValues: { ':incr': 1, ':zero': 0 },
+ ReturnValues: 'ALL_NEW',
+ })
+ .promise();
const reqs = Attributes && Attributes.reqs;
diff --git a/apps/ai-image-tagging/lambda/usage.test.js b/apps/ai-image-tagging/lambda/usage.test.js
index 6b74b540b84..842a597b6ed 100644
--- a/apps/ai-image-tagging/lambda/usage.test.js
+++ b/apps/ai-image-tagging/lambda/usage.test.js
@@ -14,7 +14,7 @@ describe('usage', () => {
test('reports usage', async () => {
global.Date = class extends OriginalDate {
- constructor () {
+ constructor() {
return new OriginalDate(2015, 1, 15);
}
};
@@ -32,7 +32,7 @@ describe('usage', () => {
UpdateExpression: 'SET #attr = if_not_exists(#attr, :zero) + :incr',
ExpressionAttributeNames: { '#attr': 'reqs' },
ExpressionAttributeValues: { ':incr': 1, ':zero': 0 },
- ReturnValues: 'ALL_NEW'
+ ReturnValues: 'ALL_NEW',
});
});
});
diff --git a/apps/brandfolder/src/index.js b/apps/brandfolder/src/index.js
index c9285cc9daa..7015d17d3c1 100644
--- a/apps/brandfolder/src/index.js
+++ b/apps/brandfolder/src/index.js
@@ -7,22 +7,22 @@ const BF_EMBED_URL = `https://integration-panel-ui.brandfolder-svc.com?channel=m
const CTA = 'Select an asset on Brandfolder';
const FIELDS_TO_PERSIST = [
- 'asset',
- 'cdn_url',
- 'mux_hls_url',
- 'extension',
- 'filename',
- 'height',
- 'id',
- 'included',
- 'mimetype',
- 'position',
- 'relationships',
- 'size',
- 'thumbnail_url',
- 'type',
- 'url',
- 'width',
+ 'asset',
+ 'cdn_url',
+ 'mux_hls_url',
+ 'extension',
+ 'filename',
+ 'height',
+ 'id',
+ 'included',
+ 'mimetype',
+ 'position',
+ 'relationships',
+ 'size',
+ 'thumbnail_url',
+ 'type',
+ 'url',
+ 'width',
];
function makeThumbnail(attachment) {
@@ -54,7 +54,7 @@ function renderDialog(sdk) {
sdk.window.startAutoResizer();
- window.addEventListener('message', e => {
+ window.addEventListener('message', (e) => {
if (e.source !== iframe.contentWindow) {
return;
}
@@ -64,7 +64,7 @@ function renderDialog(sdk) {
sdk.close([payload]);
} else if (event === 'selectedAsset' && payload.attachments.length !== 0) {
const att_id = payload.attachments[0].id;
- const attachment = payload.included.find(att => att.id === att_id);
+ const attachment = payload.included.find((att) => att.id === att_id);
if (attachment) {
sdk.close([attachment]);
}
@@ -80,7 +80,7 @@ async function openDialog(sdk, _currentValue, config) {
shouldCloseOnEscapePress: true,
parameters: { ...config },
width: 400,
- allowHeightOverflow: true
+ allowHeightOverflow: true,
});
if (!Array.isArray(result)) {
@@ -105,7 +105,7 @@ async function openDialog(sdk, _currentValue, config) {
// url: "https://s3.amazonaws.com/bf.boulder.prod/pfbfh7-f4zem0-dpcdo2/original/brandfolder-icon.png"
// width: 312
// }]
- return result.map(asset => pick(asset, FIELDS_TO_PERSIST));
+ return result.map((asset) => pick(asset, FIELDS_TO_PERSIST));
}
setup({
@@ -122,12 +122,12 @@ setup({
name: 'Brandfolder API key',
description:
'If you want to use just one API key (https://brandfolder.com/profile#integrations) for all users, enter it here.',
- required: false
- }
+ required: false,
+ },
],
validateParameters: () => {},
makeThumbnail,
renderDialog,
openDialog,
- isDisabled: () => false
+ isDisabled: () => false,
});
diff --git a/apps/bynder/src/index.js b/apps/bynder/src/index.js
index 181f3a127d1..85ee904763a 100644
--- a/apps/bynder/src/index.js
+++ b/apps/bynder/src/index.js
@@ -5,7 +5,7 @@ import logo from './logo.svg';
const CTA = 'Select a file on Bynder';
-const BYNDER_BASE_URL = "https://d8ejoa1fys2rk.cloudfront.net";
+const BYNDER_BASE_URL = 'https://d8ejoa1fys2rk.cloudfront.net';
const BYNDER_SDK_URL = `${BYNDER_BASE_URL}/5.0.5/modules/compactview/bynder-compactview-2-latest.js`;
const FIELDS_TO_PERSIST = [
@@ -29,7 +29,7 @@ const FIELDS_TO_PERSIST = [
'type',
'watermarked',
'width',
- "videoPreviewURLs"
+ 'videoPreviewURLs',
];
const FIELD_SELECTION = `
@@ -79,56 +79,59 @@ function prepareBynderHTML() {
function transformAsset(asset) {
const thumbnails = {
- "webimage": asset.files.webImage?.url,
- "thul": asset.files.thumbnail?.url
- }
+ webimage: asset.files.webImage?.url,
+ thul: asset.files.thumbnail?.url,
+ };
Object.entries(asset.files)
- .filter(([name]) => !["webImage", "thumbnail"].includes(name))
- .forEach(([key, value]) => thumbnails[key] = value?.url);
-
- return ({
- "id": asset.databaseId,
- "orientation": asset.orientation.toLowerCase(),
- "archive": asset.isArchived ? 1 : 0,
- "type": asset.type.toLowerCase(),
- "fileSize": asset.fileSize,
- "description": asset.description,
- "name": asset.name,
- "height": asset.height,
- "width": asset.width,
- "copyright": asset.copyright,
- "extension": asset.extensions,
- "userCreated": asset.createdBy,
- "datePublished": asset.publishedAt,
- "dateCreated": asset.createdAt,
- "dateModified": asset.updatedAt,
- "watermarked": asset.isWatermarked ? 1 : 0,
- "limited": asset.isLimitedUse ? 1 : 0,
- "isPublic": asset.isPublic ? 1 : 0,
- "brandId": asset.brandId,
- "thumbnails": thumbnails,
- "original": asset.originalUrl,
- "videoPreviewURLs": asset.previewUrls || []
- })
+ .filter(([name]) => !['webImage', 'thumbnail'].includes(name))
+ .forEach(([key, value]) => (thumbnails[key] = value?.url));
+
+ return {
+ id: asset.databaseId,
+ orientation: asset.orientation.toLowerCase(),
+ archive: asset.isArchived ? 1 : 0,
+ type: asset.type.toLowerCase(),
+ fileSize: asset.fileSize,
+ description: asset.description,
+ name: asset.name,
+ height: asset.height,
+ width: asset.width,
+ copyright: asset.copyright,
+ extension: asset.extensions,
+ userCreated: asset.createdBy,
+ datePublished: asset.publishedAt,
+ dateCreated: asset.createdAt,
+ dateModified: asset.updatedAt,
+ watermarked: asset.isWatermarked ? 1 : 0,
+ limited: asset.isLimitedUse ? 1 : 0,
+ isPublic: asset.isPublic ? 1 : 0,
+ brandId: asset.brandId,
+ thumbnails: thumbnails,
+ original: asset.originalUrl,
+ videoPreviewURLs: asset.previewUrls || [],
+ };
}
function checkMessageEvent(e) {
if (e.origin !== BYNDER_BASE_URL) {
- e.stopImmediatePropagation()
+ e.stopImmediatePropagation();
}
}
function renderDialog(sdk) {
const config = sdk.parameters.invocation;
- const { assetTypes, bynderURL } = config
+ const { assetTypes, bynderURL } = config;
let types = [];
if (!assetTypes) {
// We default to just images in this fallback since this is the behavior the App had in its initial release
types = ['IMAGE'];
} else {
- types = assetTypes.trim().split(',').map((type) => type.toUpperCase());
+ types = assetTypes
+ .trim()
+ .split(',')
+ .map((type) => type.toUpperCase());
}
const script = document.createElement('script');
@@ -142,24 +145,24 @@ function renderDialog(sdk) {
sdk.window.startAutoResizer();
- window.addEventListener("message", checkMessageEvent)
+ window.addEventListener('message', checkMessageEvent);
function onSuccess(assets, selected) {
sdk.close(Array.isArray(assets) ? assets.map(transformAsset) : []);
- window.removeEventListener("message", checkMessageEvent)
+ window.removeEventListener('message', checkMessageEvent);
}
- script.addEventListener("load", () => {
+ script.addEventListener('load', () => {
window.BynderCompactView.open({
- language: "en_US",
- mode: "MultiSelect",
+ language: 'en_US',
+ mode: 'MultiSelect',
assetTypes: types,
portal: { url: bynderURL, editable: true },
assetFieldSelection: FIELD_SELECTION,
- container: document.getElementById("bynder-compactview"),
- onSuccess: onSuccess
+ container: document.getElementById('bynder-compactview'),
+ onSuccess: onSuccess,
});
- })
+ });
}
async function openDialog(sdk, _currentValue, config) {
@@ -169,16 +172,16 @@ async function openDialog(sdk, _currentValue, config) {
shouldCloseOnOverlayClick: true,
shouldCloseOnEscapePress: true,
parameters: { ...config },
- width: 1400
+ width: 1400,
});
if (!Array.isArray(result)) {
return [];
}
- return result.map(item => ({
+ return result.map((item) => ({
...pick(item, FIELDS_TO_PERSIST),
- src: item.thumbnails && item.thumbnails.webimage
+ src: item.thumbnails && item.thumbnails.webimage,
}));
}
@@ -188,17 +191,20 @@ function isDisabled() {
function validateParameters({ bynderURL, assetTypes }) {
const hasValidProtocol = bynderURL.startsWith('https://');
- const isHTMLSafe = ['"', '<', '>'].every(unsafe => !bynderURL.includes(unsafe));
+ const isHTMLSafe = ['"', '<', '>'].every((unsafe) => !bynderURL.includes(unsafe));
if (!hasValidProtocol || !isHTMLSafe) {
return 'Provide a valid Bynder URL.';
}
- const types = assetTypes.trim().split(',').map(type => type.trim());
- const isAssetTypesValid = types.every(type => validAssetTypes.includes(type));
+ const types = assetTypes
+ .trim()
+ .split(',')
+ .map((type) => type.trim());
+ const isAssetTypesValid = types.every((type) => validAssetTypes.includes(type));
if (!isAssetTypesValid) {
- return `Only valid asset types may be selected: ${validAssetTypes.join(',')}`
+ return `Only valid asset types may be selected: ${validAssetTypes.join(',')}`;
}
return null;
@@ -213,24 +219,24 @@ setup({
'The Bynder app is a widget that allows editors to select media from their Bynder account. Select or upload a file on Bynder and designate the assets that you want your entry to reference.',
parameterDefinitions: [
{
- "id": "bynderURL",
- "type": "Symbol",
- "name": "Bynder URL",
- "description": "Provide Bynder URL of your account.",
- "required": true
+ id: 'bynderURL',
+ type: 'Symbol',
+ name: 'Bynder URL',
+ description: 'Provide Bynder URL of your account.',
+ required: true,
},
{
- "id": "assetTypes",
- "type": "Symbol",
- "name": "Asset types",
- "description": "Choose which types of assets can be selected.",
- "default": validAssetTypes.join(','),
- "required": true
- }
+ id: 'assetTypes',
+ type: 'Symbol',
+ name: 'Asset types',
+ description: 'Choose which types of assets can be selected.',
+ default: validAssetTypes.join(','),
+ required: true,
+ },
],
makeThumbnail,
renderDialog,
openDialog,
isDisabled,
- validateParameters
+ validateParameters,
});
diff --git a/apps/cloudinary/src/index.js b/apps/cloudinary/src/index.js
index cbe734298db..f1a54ab6a59 100644
--- a/apps/cloudinary/src/index.js
+++ b/apps/cloudinary/src/index.js
@@ -41,29 +41,29 @@ const FIELDS_TO_PERSIST = [
'resource_type',
'original_url',
'original_secure_url',
- 'raw_transformation'
+ 'raw_transformation',
];
function makeThumbnail(resource, config) {
const cloudinary = new cloudinaryCore({
cloud_name: config.cloudName,
- api_key: config.apiKey
+ api_key: config.apiKey,
});
let url;
- resource.raw_transformation = resource.raw_transformation || "";
+ resource.raw_transformation = resource.raw_transformation || '';
const alt = [resource.public_id, ...(resource.tags || [])].join(', ');
let transformations = `${resource.raw_transformation}/c_fill,h_100,w_150`;
if (resource.resource_type === 'image' && VALID_IMAGE_FORMATS.includes(resource.format)) {
url = cloudinary.url(resource.public_id, {
type: resource.type,
- rawTransformation: transformations
+ rawTransformation: transformations,
});
} else if (resource.resource_type === 'video') {
url = cloudinary.video_thumbnail_url(resource.public_id, {
type: resource.type,
- rawTransformation: transformations
+ rawTransformation: transformations,
});
}
@@ -74,15 +74,15 @@ function renderDialog(sdk) {
const { cloudinary } = window;
const config = sdk.parameters.invocation;
- const transformations = []
+ const transformations = [];
// Handle format
- if(config.format!=='none'){
+ if (config.format !== 'none') {
transformations.push({ fetch_format: config.format });
}
// Handle quality
- if(config.quality!=='none'){
+ if (config.quality !== 'none') {
transformations.push({ quality: config.quality });
}
@@ -93,19 +93,18 @@ function renderDialog(sdk) {
multiple: config.maxFiles > 1,
inline_container: '#root',
remove_header: true,
- default_transformations: [transformations]
+ default_transformations: [transformations],
};
-
const instance = cloudinary.createMediaLibrary(options, {
- insertHandler: data => sdk.close(data)
+ insertHandler: (data) => sdk.close(data),
});
const showOptions = {};
if (typeof config.startFolder === 'string' && config.startFolder.length) {
- showOptions.folder = {path: config.startFolder};
+ showOptions.folder = { path: config.startFolder };
}
-
+
instance.show(showOptions);
sdk.window.updateHeight(window.outerHeight);
@@ -120,28 +119,28 @@ async function openDialog(sdk, currentValue, config) {
shouldCloseOnOverlayClick: true,
shouldCloseOnEscapePress: true,
parameters: { ...config, maxFiles },
- width: 1400
+ width: 1400,
});
if (result && Array.isArray(result.assets)) {
- return result.assets.map(asset => extractAsset(asset));
+ return result.assets.map((asset) => extractAsset(asset));
} else {
return [];
}
}
-function extractAsset(asset){
- let res = pick(asset,FIELDS_TO_PERSIST);
+function extractAsset(asset) {
+ let res = pick(asset, FIELDS_TO_PERSIST);
// if we have a derived images, we replace the URL with the derived URL and store the origianl URL seperatly
- if(asset.derived){
+ if (asset.derived) {
res = {
...res,
original_url: res.url,
original_secure_url: res.secure_url,
url: asset.derived[0].url,
secure_url: asset.derived[0].secure_url,
- raw_transformation: asset.derived[0].raw_transformation
- }
+ raw_transformation: asset.derived[0].raw_transformation,
+ };
}
return res;
}
@@ -178,57 +177,62 @@ setup({
color: '#F4B21B',
parameterDefinitions: [
{
- "id": "cloudName",
- "name": "Cloud name",
- "description": "The Cloudinary cloud name that the app will connect to.",
- "type": "Symbol",
- "required": true
+ id: 'cloudName',
+ name: 'Cloud name',
+ description: 'The Cloudinary cloud name that the app will connect to.',
+ type: 'Symbol',
+ required: true,
},
{
- "id": "apiKey",
- "name": "API key",
- "description": "The Cloduinary API Key that can be found in your Cloudinary console.",
- "type": "Symbol",
- "required": true
+ id: 'apiKey',
+ name: 'API key',
+ description: 'The Cloduinary API Key that can be found in your Cloudinary console.',
+ type: 'Symbol',
+ required: true,
},
{
- "id": "maxFiles",
- "name": "Max number of files",
- "description": "The max number of files that can be added to a single field. Must be between 1 and 25",
- "type": "Number",
- "required": false,
- "default": 10
+ id: 'maxFiles',
+ name: 'Max number of files',
+ description:
+ 'The max number of files that can be added to a single field. Must be between 1 and 25',
+ type: 'Number',
+ required: false,
+ default: 10,
},
{
- "id": "startFolder",
- "name": "Starting folder",
- "description": "A path to a folder which the Cloudinary Media Library will automatically browse to on load",
- "type": "Symbol",
- "required": false,
- "default": ""
+ id: 'startFolder',
+ name: 'Starting folder',
+ description:
+ 'A path to a folder which the Cloudinary Media Library will automatically browse to on load',
+ type: 'Symbol',
+ required: false,
+ default: '',
},
{
- "id": "quality",
- "name": "Media Quality",
- "description": "The quality level of your assets. This can be a fixed number ranging from 1-100, or you can get Cloudinary to decide the most optimized level by setting it to 'auto'. More options are available such as: auto:low/auto:eco/auto:good/auto:best. If you wish to use the original level, set it to 'none'.",
- "type": "List",
- "value": "auto,none,auto:low,auto:eco,auto:good,auto:best,10,20,30,40,50,60,70,80,90,100",
- "required": true,
- "default": "auto"
+ id: 'quality',
+ name: 'Media Quality',
+ description:
+ "The quality level of your assets. This can be a fixed number ranging from 1-100, or you can get Cloudinary to decide the most optimized level by setting it to 'auto'. More options are available such as: auto:low/auto:eco/auto:good/auto:best. If you wish to use the original level, set it to 'none'.",
+ type: 'List',
+ value: 'auto,none,auto:low,auto:eco,auto:good,auto:best,10,20,30,40,50,60,70,80,90,100',
+ required: true,
+ default: 'auto',
},
{
- "id": "format",
- "name": "Format",
- "description": "The format of the assets. This can be set manually to a specific format - 'jpg' as an example (all supported formats can be found here - https://cloudinary.com/documentation/image_transformations#supported_image_formats. By setting it to 'auto', Cloudinary will decide on the most optimized format for your users. If you wish to keep the original format, set it to 'none'.",
- "type": "List",
- "value":"auto,none,gif,webp,bmp,flif,heif,heic,ico,jpg,jpe,jpeg,jp2,wdp,jxr,hdp,png,psd,arw,cr2,svg,tga,tif,tiff",
- "required": true,
- "default": "auto"
- }
+ id: 'format',
+ name: 'Format',
+ description:
+ "The format of the assets. This can be set manually to a specific format - 'jpg' as an example (all supported formats can be found here - https://cloudinary.com/documentation/image_transformations#supported_image_formats. By setting it to 'auto', Cloudinary will decide on the most optimized format for your users. If you wish to keep the original format, set it to 'none'.",
+ type: 'List',
+ value:
+ 'auto,none,gif,webp,bmp,flif,heif,heic,ico,jpg,jpe,jpeg,jp2,wdp,jxr,hdp,png,psd,arw,cr2,svg,tga,tif,tiff',
+ required: true,
+ default: 'auto',
+ },
],
makeThumbnail,
renderDialog,
openDialog,
isDisabled,
- validateParameters
+ validateParameters,
});
diff --git a/apps/commercelayer/src/dataTransformer.js b/apps/commercelayer/src/dataTransformer.js
index 1c0a09a334c..8eec84f3e57 100644
--- a/apps/commercelayer/src/dataTransformer.js
+++ b/apps/commercelayer/src/dataTransformer.js
@@ -4,7 +4,7 @@ import get from 'lodash/get';
* Transforms the API response of CommerceLayer into
* the product schema expected by the SkuPicker component
*/
-export const dataTransformer = projectUrl => product => {
+export const dataTransformer = (projectUrl) => (product) => {
const { id } = product;
const image = get(product, ['imageUrl']) || get(product, ['attributes', 'image_url']);
const name = get(product, ['name']) || get(product, ['attributes', 'name']);
@@ -14,6 +14,6 @@ export const dataTransformer = projectUrl => product => {
image,
name,
sku,
- externalLink: `${projectUrl}/admin/skus/${id}/edit`
+ externalLink: `${projectUrl}/admin/skus/${id}/edit`,
};
};
diff --git a/apps/commercelayer/src/index.js b/apps/commercelayer/src/index.js
index 384974aae7b..50e9d6c490e 100644
--- a/apps/commercelayer/src/index.js
+++ b/apps/commercelayer/src/index.js
@@ -40,7 +40,7 @@ async function getAccessToken(clientId, endpoint) {
// CLayerAuth SDK will throw if not present. By setting to empty
// string we prevent the SDK exception and the value is ignored
// by the Commerce Layer Auth API.
- clientSecret: ''
+ clientSecret: '',
})
).accessToken;
}
@@ -64,16 +64,16 @@ async function fetchSKUs(installationParams, search, pagination) {
const { clientId, apiEndpoint } = installationParams;
const accessToken = await getAccessToken(clientId, apiEndpoint);
- const URL = `${apiEndpoint}/api/skus?page[size]=${PER_PAGE}&page[number]=${pagination.offset /
- PER_PAGE +
- 1}${search.length ? `&filter[q][name_or_code_cont]=${search}` : ''}`;
+ const URL = `${apiEndpoint}/api/skus?page[size]=${PER_PAGE}&page[number]=${
+ pagination.offset / PER_PAGE + 1
+ }${search.length ? `&filter[q][name_or_code_cont]=${search}` : ''}`;
const res = await fetch(URL, {
headers: {
Accept: 'application/vnd.api+json',
- Authorization: `Bearer ${accessToken}`
+ Authorization: `Bearer ${accessToken}`,
},
- method: 'GET'
+ method: 'GET',
});
return await res.json();
@@ -96,14 +96,14 @@ const fetchProductPreviews = async function fetchProductPreviews(skus, config) {
// Here we account for the edge case where the user has picked more than 25
// products, which is the max amount of pagination results. We need to fetch
// and compile the complete selection result doing 1 request per 25 items.
- const resultPromises = chunk(skus, PREVIEWS_PER_PAGE).map(async skusSubset => {
+ const resultPromises = chunk(skus, PREVIEWS_PER_PAGE).map(async (skusSubset) => {
const URL = `${apiEndpoint}/api/skus?page[size]=${PREVIEWS_PER_PAGE}&filter[q][code_in]=${skusSubset}`;
const res = await fetch(URL, {
headers: {
Accept: 'application/vnd.api+json',
- Authorization: `Bearer ${accessToken}`
+ Authorization: `Bearer ${accessToken}`,
},
- method: 'GET'
+ method: 'GET',
});
return await res.json();
});
@@ -116,8 +116,8 @@ const fetchProductPreviews = async function fetchProductPreviews(skus, config) {
const missingProducts = difference(
skus,
- foundProducts.map(product => product.sku)
- ).map(sku => ({ sku, isMissing: true, image: '', name: '', id: '' }));
+ foundProducts.map((product) => product.sku)
+ ).map((sku) => ({ sku, isMissing: true, image: '', name: '', id: '' }));
return [...foundProducts, ...missingProducts];
};
@@ -138,11 +138,11 @@ async function renderDialog(sdk) {
count: PER_PAGE,
limit: PER_PAGE,
total: result.meta.record_count,
- offset: pagination.offset
+ offset: pagination.offset,
},
- products: result.data.map(dataTransformer(sdk.parameters.installation.apiEndpoint))
+ products: result.data.map(dataTransformer(sdk.parameters.installation.apiEndpoint)),
};
- }
+ },
});
sdk.window.startAutoResizer();
@@ -156,7 +156,7 @@ async function openDialog(sdk, currentValue, config) {
shouldCloseOnOverlayClick: true,
shouldCloseOnEscapePress: true,
parameters: config,
- width: 1400
+ width: 1400,
});
return Array.isArray(skus) ? skus : [];
@@ -180,19 +180,19 @@ setup({
name: 'Client ID',
description: 'The client ID',
type: 'Symbol',
- required: true
+ required: true,
},
{
id: 'apiEndpoint',
name: 'API Endpoint',
description: 'The Commerce Layer API endpoint',
type: 'Symbol',
- required: true
- }
+ required: true,
+ },
],
fetchProductPreviews,
renderDialog,
openDialog,
isDisabled,
- validateParameters
+ validateParameters,
});
diff --git a/apps/commercetools/src/AppConfig/FieldSelector/ToggleGroup.tsx b/apps/commercetools/src/AppConfig/FieldSelector/ToggleGroup.tsx
index 79ca50046ce..da355146c13 100644
--- a/apps/commercetools/src/AppConfig/FieldSelector/ToggleGroup.tsx
+++ b/apps/commercetools/src/AppConfig/FieldSelector/ToggleGroup.tsx
@@ -1,8 +1,8 @@
-import React from "react";
-import { ToggleButton } from "@contentful/forma-36-react-components";
-import { Field, PickerMode } from "../../interfaces";
-import { css } from "emotion";
-import tokens from "@contentful/forma-36-tokens";
+import React from 'react';
+import { ToggleButton } from '@contentful/forma-36-react-components';
+import { Field, PickerMode } from '../../interfaces';
+import { css } from 'emotion';
+import tokens from '@contentful/forma-36-tokens';
const styles = {
toggleGroup: (isPickerModeSetToProduct: boolean) =>
@@ -10,20 +10,20 @@ const styles = {
marginTop: tokens.spacingXs,
marginLeft: tokens.spacingL,
- "> :first-of-type": css({
+ '> :first-of-type': css({
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
- position: "relative",
- zIndex: isPickerModeSetToProduct ? 1 : 0
+ position: 'relative',
+ zIndex: isPickerModeSetToProduct ? 1 : 0,
}),
- "> :last-of-type": css({
+ '> :last-of-type': css({
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
- marginLeft: "-1px",
- position: "relative",
- zIndex: isPickerModeSetToProduct ? 0 : 1
- })
- })
+ marginLeft: '-1px',
+ position: 'relative',
+ zIndex: isPickerModeSetToProduct ? 0 : 1,
+ }),
+ }),
};
interface Props {
@@ -33,19 +33,13 @@ interface Props {
}
export function ToggleGroup({ activePickerMode, onChange, field }: Props) {
- const isPickerModeSetToProduct = activePickerMode === "product";
+ const isPickerModeSetToProduct = activePickerMode === 'product';
return (
- onChange("product")}
- isActive={isPickerModeSetToProduct}
- >
+ onChange('product')} isActive={isPickerModeSetToProduct}>
{field.type === 'Symbol' ? 'Product' : 'Products'}
- onChange("category")}
- isActive={!isPickerModeSetToProduct}
- >
+ onChange('category')} isActive={!isPickerModeSetToProduct}>
{field.type === 'Symbol' ? 'Category' : 'Categories'}
diff --git a/apps/commercetools/src/AppConfig/FieldSelector/index.tsx b/apps/commercetools/src/AppConfig/FieldSelector/index.tsx
index f5ab908e0d3..199ad1ef46b 100644
--- a/apps/commercetools/src/AppConfig/FieldSelector/index.tsx
+++ b/apps/commercetools/src/AppConfig/FieldSelector/index.tsx
@@ -13,11 +13,11 @@ import { PickerMode, ContentType } from '../../interfaces';
const styles = {
fieldGroup: css({
display: 'flex',
- flexDirection: 'column'
+ flexDirection: 'column',
}),
select: css({
- marginLeft: '10px'
- })
+ marginLeft: '10px',
+ }),
};
interface Props {
@@ -52,13 +52,13 @@ export default class FieldSelector extends React.Component {
render() {
const { compatibleFields, contentTypes, selectedFields } = this.props;
- return contentTypes.map(ct => {
+ return contentTypes.map((ct) => {
const fields = compatibleFields[ct.sys.id];
return (
diff --git a/apps/commercetools/src/Editor/Field.tsx b/apps/commercetools/src/Editor/Field.tsx
index 077c782d140..1b7f00d1a7b 100644
--- a/apps/commercetools/src/Editor/Field.tsx
+++ b/apps/commercetools/src/Editor/Field.tsx
@@ -1,14 +1,14 @@
-import * as React from "react";
-import { Button } from "@contentful/forma-36-react-components";
-import tokens from "@contentful/forma-36-tokens";
-import get from "lodash/get";
-import { css } from "emotion";
-import { FieldExtensionSDK } from "@contentful/app-sdk";
-import { ProductPreviews } from "./ProductPreviews/ProductPreviews";
-import { CategoryPreviews } from "./CategoryPreviews/CategoryPreviews";
-import { fetchProductPreviews } from "../api/fetchProductPreviews";
-import { fetchCategoryPreviews } from "../api/fetchCategoryPreviews";
-import logo from "../logo.svg";
+import * as React from 'react';
+import { Button } from '@contentful/forma-36-react-components';
+import tokens from '@contentful/forma-36-tokens';
+import get from 'lodash/get';
+import { css } from 'emotion';
+import { FieldExtensionSDK } from '@contentful/app-sdk';
+import { ProductPreviews } from './ProductPreviews/ProductPreviews';
+import { CategoryPreviews } from './CategoryPreviews/CategoryPreviews';
+import { fetchProductPreviews } from '../api/fetchProductPreviews';
+import { fetchCategoryPreviews } from '../api/fetchCategoryPreviews';
+import logo from '../logo.svg';
interface Props {
sdk: FieldExtensionSDK;
@@ -21,17 +21,17 @@ interface State {
const styles = {
sortable: css({
- marginBottom: tokens.spacingM
+ marginBottom: tokens.spacingM,
}),
container: css({
- display: "flex"
+ display: 'flex',
}),
logo: css({
- display: "block",
- width: "30px",
- height: "30px",
- marginRight: tokens.spacingM
- })
+ display: 'block',
+ width: '30px',
+ height: '30px',
+ marginRight: tokens.spacingM,
+ }),
};
function fieldValueToState(value?: string | string[]): string[] {
@@ -41,23 +41,23 @@ function fieldValueToState(value?: string | string[]): string[] {
return Array.isArray(value) ? value : [value];
}
-function makeCTAText(fieldType: string, pickerMode: "category" | "product") {
- const isArray = fieldType === "Array";
+function makeCTAText(fieldType: string, pickerMode: 'category' | 'product') {
+ const isArray = fieldType === 'Array';
const beingSelected =
- pickerMode === "category"
+ pickerMode === 'category'
? isArray
- ? "categories"
- : "a category"
+ ? 'categories'
+ : 'a category'
: isArray
- ? "products"
- : "a product";
+ ? 'products'
+ : 'a product';
return `Select ${beingSelected}`;
}
export default class Field extends React.Component {
state = {
value: fieldValueToState(this.props.sdk.field.getValue()),
- editingDisabled: true
+ editingDisabled: true,
};
componentDidMount() {
@@ -81,8 +81,8 @@ export default class Field extends React.Component {
return get(
sdk,
- ["parameters", "installation", "fieldsConfig", contentTypeId, fieldId],
- "product"
+ ['parameters', 'installation', 'fieldsConfig', contentTypeId, fieldId],
+ 'product'
);
};
@@ -90,7 +90,7 @@ export default class Field extends React.Component {
this.setState({ value: skus });
if (skus.length > 0) {
- const value = this.props.sdk.field.type === "Array" ? skus : skus[0];
+ const value = this.props.sdk.field.type === 'Array' ? skus : skus[0];
this.props.sdk.field.setValue(value);
} else {
this.props.sdk.field.removeValue();
@@ -102,7 +102,7 @@ export default class Field extends React.Component {
const skus = await sdk.dialogs.openCurrentApp({
allowHeightOverflow: true,
- position: "center",
+ position: 'center',
title: makeCTAText(sdk.field.type, this.getPickerMode()),
shouldCloseOnOverlayClick: true,
shouldCloseOnEscapePress: true,
@@ -111,9 +111,9 @@ export default class Field extends React.Component {
fieldValue: fieldValueToState(sdk.field.getValue()),
fieldType: sdk.field.type,
fieldId: sdk.field.id,
- pickerMode: this.getPickerMode()
+ pickerMode: this.getPickerMode(),
},
- width: 1400
+ width: 1400,
});
const result = Array.isArray(skus) ? skus : [];
@@ -125,10 +125,10 @@ export default class Field extends React.Component {
render = () => {
const { value: data, editingDisabled } = this.state;
- const isPickerTypeSetToCategory = this.getPickerMode() === "category";
+ const isPickerTypeSetToCategory = this.getPickerMode() === 'category';
const hasItems = data.length > 0;
const config = this.props.sdk.parameters.installation;
- const fieldType = get(this.props, ["sdk", "field", "type"], "");
+ const fieldType = get(this.props, ['sdk', 'field', 'type'], '');
return (
<>
@@ -140,9 +140,7 @@ export default class Field extends React.Component {
disabled={editingDisabled}
categories={data}
onChange={this.updateStateValue}
- fetchCategoryPreviews={categories =>
- fetchCategoryPreviews(categories, config)
- }
+ fetchCategoryPreviews={(categories) => fetchCategoryPreviews(categories, config)}
/>
) : (
{
disabled={editingDisabled}
skus={data}
onChange={this.updateStateValue}
- fetchProductPreviews={(skus: string[]) =>
- fetchProductPreviews(skus, config)
- }
+ fetchProductPreviews={(skus: string[]) => fetchProductPreviews(skus, config)}
/>
)}
diff --git a/apps/commercetools/src/Editor/ProductPreviews/ProductPreviews.tsx b/apps/commercetools/src/Editor/ProductPreviews/ProductPreviews.tsx
index 549b29a6a6d..d6f1e796a6c 100644
--- a/apps/commercetools/src/Editor/ProductPreviews/ProductPreviews.tsx
+++ b/apps/commercetools/src/Editor/ProductPreviews/ProductPreviews.tsx
@@ -21,7 +21,7 @@ interface State {
export class ProductPreviews extends React.Component