-
-
- {listings.map(l => (
-
- ))}
+
+
-
-
);
};
-ManageListingsPageComponent.defaultProps = {
- currentUser: null,
- listings: [],
- pagination: null,
- queryListingsError: null,
- queryParams: null,
- closingListing: null,
- closingListingError: null,
- openingListing: null,
- openingListingError: null,
-};
-
-ManageListingsPageComponent.propTypes = {
- currentUser: propTypes.currentUser,
- closingListing: shape({ uuid: string.isRequired }),
- closingListingError: shape({
- listingId: propTypes.uuid.isRequired,
- error: propTypes.error.isRequired,
- }),
- listings: arrayOf(propTypes.ownListing),
- onCloseListing: func.isRequired,
- onOpenListing: func.isRequired,
- openingListing: shape({ uuid: string.isRequired }),
- openingListingError: shape({
- listingId: propTypes.uuid.isRequired,
- error: propTypes.error.isRequired,
- }),
- pagination: propTypes.pagination,
- queryInProgress: bool.isRequired,
- queryListingsError: propTypes.error,
- queryParams: object,
- scrollingDisabled: bool.isRequired,
-};
-
const mapStateToProps = state => {
const { currentUser } = state.user;
const {
@@ -241,6 +272,7 @@ const mapStateToProps = state => {
openingListingError,
closingListing,
closingListingError,
+ categories,
} = state.ManageListingsPage;
const listings = getOwnListingsById(state, currentPageResultIds);
return {
@@ -256,6 +288,7 @@ const mapStateToProps = state => {
openingListingError,
closingListing,
closingListingError,
+ categories,
};
};
@@ -264,11 +297,8 @@ const mapDispatchToProps = dispatch => ({
onOpenListing: listingId => dispatch(openListing(listingId)),
});
-const ManageListingsPage = compose(
- connect(
- mapStateToProps,
- mapDispatchToProps
- )
-)(ManageListingsPageComponent);
+const ManageListingsPage = compose(connect(mapStateToProps, mapDispatchToProps))(
+ ManageListingsPageComponent
+);
export default ManageListingsPage;
diff --git a/src/containers/ManageListingsPage/ManageListingsPage.module.css b/src/containers/ManageListingsPage/ManageListingsPage.module.css
index c693c8ede..838189447 100644
--- a/src/containers/ManageListingsPage/ManageListingsPage.module.css
+++ b/src/containers/ManageListingsPage/ManageListingsPage.module.css
@@ -90,9 +90,48 @@
}
}
-.listingCards {
+.listingCardsTabs {
padding: 0 24px;
+ @media (--viewportLarge) {
+ padding: 0 36px;
+ }
+}
+
+.productTypeFilters {
+ padding: 12px 0;
+}
+
+.filters {
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.categories {
+ padding-right: 12px;
+}
+.productTypeFilters button:first-child {
+}
+
+.productTypeFilters a {
+ color: var(--colorGrey700);
+ text-decoration: none;
+ font-size: 14px;
+ text-wrap: nowrap;
+}
+
+.productTypeFilters a:hover {
+ color: var(--marketplaceColorDark);
+
+}
+
+.productTypeFilters .filterLinkActive {
+ color: var(--marketplaceColor);
+ font-weight: bold;
+}
+
+.listingCards {
+ padding-top: 24px;
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 24px;
@@ -108,7 +147,6 @@
@media (--viewportLarge) {
grid-template-columns: repeat(3, 1fr);
- padding: 0 36px;
}
}
diff --git a/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.test.js b/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.test.js
index b7a13478f..d5004ef18 100644
--- a/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.test.js
+++ b/src/containers/ProfileSettingsPage/ProfileSettingsForm/ProfileSettingsForm.test.js
@@ -165,9 +165,63 @@ const attributes = {
privateData: {
textField: 'Text field content',
},
+ metadata: {
+ isBrandAdmin: false,
+ },
},
};
+const userTypes = [
+ {
+ userType: 'buyer',
+ defaultUserFields: {
+ email: true,
+ payoutDetails: true,
+ profileImage: true,
+ paymentMethods: true,
+ password: true,
+ displayName: true,
+ firstName: true,
+ bio: true,
+ lastName: true,
+ phoneNumber: false,
+ },
+ label: 'Buyer',
+ },
+ {
+ userType: 'creative-seller',
+ defaultUserFields: {
+ email: true,
+ payoutDetails: true,
+ profileImage: true,
+ paymentMethods: true,
+ password: true,
+ displayName: true,
+ firstName: true,
+ bio: true,
+ lastName: true,
+ phoneNumber: false,
+ },
+ label: 'Creative',
+ },
+ {
+ userType: 'studio-brand',
+ defaultUserFields: {
+ email: true,
+ payoutDetails: true,
+ profileImage: true,
+ paymentMethods: true,
+ password: true,
+ displayName: true,
+ firstName: true,
+ bio: true,
+ lastName: true,
+ phoneNumber: false,
+ },
+ label: 'Brand',
+ },
+];
+
describe('ProfileSettingsForm', () => {
it('shows inputs and initial values for name', () => {
const u1 = createCurrentUser('userId');
@@ -186,6 +240,7 @@ describe('ProfileSettingsForm', () => {
firstName,
lastName,
}}
+ userTypes={userTypes}
/>
);
expect(
@@ -214,6 +269,7 @@ describe('ProfileSettingsForm', () => {
initialValues={{
bio,
}}
+ userTypes={userTypes}
/>
);
expect(
@@ -223,76 +279,6 @@ describe('ProfileSettingsForm', () => {
expect(screen.getByDisplayValue(bio)).toBeInTheDocument();
});
- it('shows a select input for enum fields', () => {
- const u1 = createCurrentUser('userId', attributes);
- const { publicData } = u1.attributes.profile;
- render(
-
- );
-
- expect(screen.getByRole('combobox', { name: 'Enum Field 1' })).toBeInTheDocument();
- expect(screen.getByRole('option', { name: 'e1l1' }).selected).toBe(true);
- });
-
- it('shows a select input for boolean fields', () => {
- const u1 = createCurrentUser('userId', attributes);
- const { publicData } = u1.attributes.profile;
- render(
-
- );
-
- expect(screen.getByRole('combobox', { name: 'Boolean Field' })).toBeInTheDocument();
- expect(screen.getByRole('option', { name: 'FieldBoolean.yes' }).selected).toBe(true);
- });
-
- it('shows a numeric input for long fields', () => {
- const u1 = createCurrentUser('userId', attributes);
- const { publicData } = u1.attributes.profile;
- render(
-
- );
-
- const longInput = screen.getByRole('spinbutton', { name: 'Long Field' });
- expect(longInput).toBeInTheDocument();
- expect(longInput).toHaveValue(123);
- });
-
it('shows a textbox input for text fields', () => {
const u1 = createCurrentUser('userId', attributes);
const { privateData } = u1.attributes.profile;
@@ -309,6 +295,7 @@ describe('ProfileSettingsForm', () => {
initialValues={{
...initialValuesForUserFields(privateData, 'private', 'a', userFieldConfig),
}}
+ userTypes={userTypes}
/>
);
@@ -333,6 +320,7 @@ describe('ProfileSettingsForm', () => {
initialValues={{
...initialValuesForUserFields(publicData, 'public', 'a', userFieldConfig),
}}
+ userTypes={userTypes}
/>
);
@@ -344,35 +332,6 @@ describe('ProfileSettingsForm', () => {
expect(screen.getByRole('checkbox', { name: 'ml3' }).checked).toBe(false);
});
- it('shows inputs and initial values for custom user fields by user type', () => {
- const u1 = createCurrentUser('userId', attributes);
- const { publicData } = u1.attributes.profile;
- render(
-
- );
-
- expect(screen.getByText('Enum Field 1')).toBeInTheDocument();
- expect(screen.getByDisplayValue('e1l1')).toBeInTheDocument();
- expect(screen.getByText('Long Field')).toBeInTheDocument();
- expect(screen.getByDisplayValue(123)).toBeInTheDocument();
-
- // Don't show enum field 2 and boolean field for user type a even if it is in public data
- expect(screen.queryByText('Enum Field 2')).toBeNull();
- expect(screen.queryByText('Boolean Field')).toBeNull();
- });
-
it('only shows non-restricted inputs and values for custom user fields if no user type specified', () => {
const u1 = createCurrentUser('userId', attributes);
const { publicData, privateData } = u1.attributes.profile;
@@ -390,6 +349,7 @@ describe('ProfileSettingsForm', () => {
...initialValuesForUserFields(publicData, 'public', null, userFieldConfig),
...initialValuesForUserFields(privateData, 'private', null, userFieldConfig),
}}
+ userTypes={userTypes}
/>
);
@@ -405,64 +365,4 @@ describe('ProfileSettingsForm', () => {
expect(screen.queryByText('Long Field')).toBeNull();
expect(screen.queryByDisplayValue(123)).toBeNull();
});
-
- it('enables Save button when required fields are filled', () => {
- const user = createCurrentUser('userId');
- const u1 = {
- ...user,
- attributes: {
- ...user.attributes,
- profile: {
- ...user.attributes.profile,
- ...attributes.profile,
- },
- },
- };
-
- const { firstName, lastName, publicData } = u1.attributes.profile;
- render(
-
- );
-
- // Save button should be disabled before changes.
- expect(screen.getByRole('button', { name: 'ProfileSettingsForm.saveChanges' })).toBeDisabled();
-
- // Expect the initial values to be in the document before any changes
- expect(screen.getByText('ProfileSettingsForm.firstNameLabel')).toBeInTheDocument();
- expect(screen.getByDisplayValue(firstName)).toBeInTheDocument();
- expect(screen.getByText('ProfileSettingsForm.lastNameLabel')).toBeInTheDocument();
- expect(screen.getByDisplayValue(lastName)).toBeInTheDocument();
- expect(screen.getByText('Multi-enum Field')).toBeInTheDocument();
- expect(screen.getByRole('checkbox', { name: 'ml1' }).checked).toBe(true);
- expect(screen.getByRole('checkbox', { name: 'ml2' }).checked).toBe(true);
-
- // Two required fields missing, textField and textField2.
- // First, enter text into textField and check that the save button remains
- // disabled, since another required field is still empty
- const textInput1 = screen.getByRole('textbox', { name: 'Text Field' });
- userEvent.type(textInput1, 'Text 1 value');
- expect(textInput1).toHaveValue('Text 1 value');
- expect(screen.getByRole('button', { name: 'ProfileSettingsForm.saveChanges' })).toBeDisabled();
-
- // Enter text into the other required text field, and check that the
- // save button is now enabled, since all required fields have values.
- const textInput2 = screen.getByRole('textbox', { name: 'Text Field 2' });
- userEvent.type(textInput2, 'Text 2 value');
- expect(textInput2).toHaveValue('Text 2 value');
- expect(screen.getByRole('button', { name: 'ProfileSettingsForm.saveChanges' })).toBeEnabled();
- });
});
diff --git a/src/containers/SearchPage/SearchMap/ReusableMapContainer.js b/src/containers/SearchPage/SearchMap/ReusableMapContainer.js
index bd1631106..b519bdeb5 100644
--- a/src/containers/SearchPage/SearchMap/ReusableMapContainer.js
+++ b/src/containers/SearchPage/SearchMap/ReusableMapContainer.js
@@ -1,10 +1,10 @@
import React from 'react';
-import ReactDOM from 'react-dom';
-import { node, string, object } from 'prop-types';
+import { node, object, string } from 'prop-types';
import { IntlProvider } from '../../../util/reactIntl';
import css from './SearchMap.module.css';
+import { createRoot } from 'react-dom/client';
/**
* ReusableMapContainer makes Google Map usage more effective. This improves:
@@ -65,8 +65,9 @@ class ReusableMapContainer extends React.Component {
);
+ const root = createRoot(this.el);
// Render children to created element
- ReactDOM.render(children, this.el);
+ root.render(children);
};
const targetDomNode = document.getElementById(this.el.id);
diff --git a/src/containers/TopbarContainer/Topbar/TopbarDesktop/CustomLinksMenu/CustomLinksMenu.js b/src/containers/TopbarContainer/Topbar/TopbarDesktop/CustomLinksMenu/CustomLinksMenu.js
index b9387392f..f46d6f813 100644
--- a/src/containers/TopbarContainer/Topbar/TopbarDesktop/CustomLinksMenu/CustomLinksMenu.js
+++ b/src/containers/TopbarContainer/Topbar/TopbarDesktop/CustomLinksMenu/CustomLinksMenu.js
@@ -64,6 +64,10 @@ const groupMeasuredLinks = (links, containerWidth, menuMoreWidth) => {
};
const calculateContainerWidth = (containerRefTarget, parentWidth) => {
+ if (!containerRefTarget) {
+ return;
+ }
+
// Siblings include logo, search form, (inbox, profile menu || login signup)
const siblingArray = containerRefTarget?.parentNode?.childNodes
? Array.from(containerRefTarget.parentNode.childNodes).filter(n => n !== containerRefTarget)
diff --git a/src/containers/TopbarContainer/Topbar/TopbarDesktop/TopbarDesktop.js b/src/containers/TopbarContainer/Topbar/TopbarDesktop/TopbarDesktop.js
index 2f4720152..5d3d82c44 100644
--- a/src/containers/TopbarContainer/Topbar/TopbarDesktop/TopbarDesktop.js
+++ b/src/containers/TopbarContainer/Topbar/TopbarDesktop/TopbarDesktop.js
@@ -76,7 +76,7 @@ const ProfileMenu = ({ currentPage, currentUser, onLogout }) => {
name="ManageListingsPage"
>
-
+